@memberjunction/metadata-sync 2.55.0 → 2.56.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 (64) hide show
  1. package/README.md +92 -51
  2. package/dist/index.d.ts +21 -1
  3. package/dist/index.js +41 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/file-backup-manager.js +2 -2
  6. package/dist/lib/file-backup-manager.js.map +1 -1
  7. package/dist/lib/sql-logger.d.ts +44 -0
  8. package/dist/lib/sql-logger.js +140 -0
  9. package/dist/lib/sql-logger.js.map +1 -0
  10. package/dist/lib/sync-engine.js +2 -2
  11. package/dist/lib/sync-engine.js.map +1 -1
  12. package/dist/lib/transaction-manager.d.ts +36 -0
  13. package/dist/lib/transaction-manager.js +117 -0
  14. package/dist/lib/transaction-manager.js.map +1 -0
  15. package/dist/services/FileResetService.d.ts +30 -0
  16. package/dist/services/FileResetService.js +182 -0
  17. package/dist/services/FileResetService.js.map +1 -0
  18. package/dist/services/InitService.d.ts +17 -0
  19. package/dist/services/InitService.js +118 -0
  20. package/dist/services/InitService.js.map +1 -0
  21. package/dist/services/PullService.d.ts +45 -0
  22. package/dist/services/PullService.js +564 -0
  23. package/dist/services/PullService.js.map +1 -0
  24. package/dist/services/PushService.d.ts +45 -0
  25. package/dist/services/PushService.js +394 -0
  26. package/dist/services/PushService.js.map +1 -0
  27. package/dist/services/StatusService.d.ts +32 -0
  28. package/dist/services/StatusService.js +138 -0
  29. package/dist/services/StatusService.js.map +1 -0
  30. package/dist/services/WatchService.d.ts +32 -0
  31. package/dist/services/WatchService.js +242 -0
  32. package/dist/services/WatchService.js.map +1 -0
  33. package/dist/services/index.d.ts +16 -0
  34. package/dist/services/index.js +28 -0
  35. package/dist/services/index.js.map +1 -0
  36. package/package.json +14 -45
  37. package/bin/debug.js +0 -7
  38. package/bin/run +0 -17
  39. package/bin/run.js +0 -6
  40. package/dist/commands/file-reset/index.d.ts +0 -15
  41. package/dist/commands/file-reset/index.js +0 -221
  42. package/dist/commands/file-reset/index.js.map +0 -1
  43. package/dist/commands/init/index.d.ts +0 -7
  44. package/dist/commands/init/index.js +0 -155
  45. package/dist/commands/init/index.js.map +0 -1
  46. package/dist/commands/pull/index.d.ts +0 -246
  47. package/dist/commands/pull/index.js +0 -1448
  48. package/dist/commands/pull/index.js.map +0 -1
  49. package/dist/commands/push/index.d.ts +0 -41
  50. package/dist/commands/push/index.js +0 -1129
  51. package/dist/commands/push/index.js.map +0 -1
  52. package/dist/commands/status/index.d.ts +0 -10
  53. package/dist/commands/status/index.js +0 -199
  54. package/dist/commands/status/index.js.map +0 -1
  55. package/dist/commands/validate/index.d.ts +0 -15
  56. package/dist/commands/validate/index.js +0 -149
  57. package/dist/commands/validate/index.js.map +0 -1
  58. package/dist/commands/watch/index.d.ts +0 -15
  59. package/dist/commands/watch/index.js +0 -300
  60. package/dist/commands/watch/index.js.map +0 -1
  61. package/dist/hooks/init.d.ts +0 -3
  62. package/dist/hooks/init.js +0 -59
  63. package/dist/hooks/init.js.map +0 -1
  64. package/oclif.manifest.json +0 -376
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileResetService = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const fast_glob_1 = __importDefault(require("fast-glob"));
10
+ const config_1 = require("../config");
11
+ const config_manager_1 = require("../lib/config-manager");
12
+ class FileResetService {
13
+ async resetFiles(options = {}, callbacks) {
14
+ const sections = options.sections || 'both';
15
+ // Load sync config
16
+ const syncConfig = await (0, config_1.loadSyncConfig)(config_manager_1.configManager.getOriginalCwd());
17
+ if (!syncConfig) {
18
+ throw new Error('No .mj-sync.json found in current directory');
19
+ }
20
+ // Find all metadata JSON files
21
+ callbacks?.onProgress?.('Finding metadata files');
22
+ const pattern = syncConfig.filePattern || '.*.json';
23
+ const files = await (0, fast_glob_1.default)(pattern, {
24
+ cwd: config_manager_1.configManager.getOriginalCwd(),
25
+ absolute: true,
26
+ ignore: ['.mj-sync.json', '.mj-folder.json'],
27
+ });
28
+ if (files.length === 0) {
29
+ callbacks?.onLog?.('No metadata files found');
30
+ return {
31
+ processedFiles: 0,
32
+ modifiedFiles: 0,
33
+ totalPrimaryKeys: 0,
34
+ totalSyncs: 0,
35
+ filesWithPrimaryKey: 0,
36
+ filesWithSync: 0,
37
+ backupsCreated: 0
38
+ };
39
+ }
40
+ callbacks?.onLog?.(`Found ${files.length} metadata file${files.length === 1 ? '' : 's'}`);
41
+ // Count what will be removed
42
+ let filesWithPrimaryKey = 0;
43
+ let filesWithSync = 0;
44
+ let totalPrimaryKeys = 0;
45
+ let totalSyncs = 0;
46
+ for (const file of files) {
47
+ const content = await fs_extra_1.default.readJson(file);
48
+ const stats = this.countSections(content);
49
+ if (stats.primaryKeyCount > 0) {
50
+ filesWithPrimaryKey++;
51
+ totalPrimaryKeys += stats.primaryKeyCount;
52
+ }
53
+ if (stats.syncCount > 0) {
54
+ filesWithSync++;
55
+ totalSyncs += stats.syncCount;
56
+ }
57
+ }
58
+ // Report what will be removed
59
+ if (sections === 'both' || sections === 'primaryKey') {
60
+ callbacks?.onLog?.(`Will remove ${totalPrimaryKeys} primaryKey section${totalPrimaryKeys === 1 ? '' : 's'} from ${filesWithPrimaryKey} file${filesWithPrimaryKey === 1 ? '' : 's'}`);
61
+ }
62
+ if (sections === 'both' || sections === 'sync') {
63
+ callbacks?.onLog?.(`Will remove ${totalSyncs} sync section${totalSyncs === 1 ? '' : 's'} from ${filesWithSync} file${filesWithSync === 1 ? '' : 's'}`);
64
+ }
65
+ if (options.dryRun) {
66
+ callbacks?.onLog?.('Dry run mode - no files will be modified');
67
+ if (options.verbose) {
68
+ for (const file of files) {
69
+ const content = await fs_extra_1.default.readJson(file);
70
+ const stats = this.countSections(content);
71
+ if (stats.primaryKeyCount > 0 || stats.syncCount > 0) {
72
+ callbacks?.onLog?.(`${path_1.default.relative(config_manager_1.configManager.getOriginalCwd(), file)}:`);
73
+ if (stats.primaryKeyCount > 0) {
74
+ callbacks?.onLog?.(` - ${stats.primaryKeyCount} primaryKey section${stats.primaryKeyCount === 1 ? '' : 's'}`);
75
+ }
76
+ if (stats.syncCount > 0) {
77
+ callbacks?.onLog?.(` - ${stats.syncCount} sync section${stats.syncCount === 1 ? '' : 's'}`);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return {
83
+ processedFiles: files.length,
84
+ modifiedFiles: 0,
85
+ totalPrimaryKeys,
86
+ totalSyncs,
87
+ filesWithPrimaryKey,
88
+ filesWithSync,
89
+ backupsCreated: 0
90
+ };
91
+ }
92
+ // Process files
93
+ callbacks?.onProgress?.('Processing files');
94
+ let processedFiles = 0;
95
+ let modifiedFiles = 0;
96
+ let backupsCreated = 0;
97
+ for (const file of files) {
98
+ processedFiles++;
99
+ const content = await fs_extra_1.default.readJson(file);
100
+ const originalContent = JSON.stringify(content);
101
+ // Remove sections
102
+ const cleanedContent = this.removeSections(content, sections);
103
+ // Only write if content changed
104
+ if (JSON.stringify(cleanedContent) !== originalContent) {
105
+ // Create backup if requested
106
+ if (!options.noBackup) {
107
+ const backupPath = `${file}.backup`;
108
+ await fs_extra_1.default.writeJson(backupPath, content, { spaces: 2 });
109
+ backupsCreated++;
110
+ }
111
+ // Write cleaned content
112
+ await fs_extra_1.default.writeJson(file, cleanedContent, { spaces: 2 });
113
+ modifiedFiles++;
114
+ if (options.verbose) {
115
+ callbacks?.onLog?.(`✓ ${path_1.default.relative(config_manager_1.configManager.getOriginalCwd(), file)}`);
116
+ }
117
+ }
118
+ }
119
+ return {
120
+ processedFiles,
121
+ modifiedFiles,
122
+ totalPrimaryKeys,
123
+ totalSyncs,
124
+ filesWithPrimaryKey,
125
+ filesWithSync,
126
+ backupsCreated
127
+ };
128
+ }
129
+ countSections(data) {
130
+ let primaryKeyCount = 0;
131
+ let syncCount = 0;
132
+ if (Array.isArray(data)) {
133
+ for (const item of data) {
134
+ const stats = this.countSections(item);
135
+ primaryKeyCount += stats.primaryKeyCount;
136
+ syncCount += stats.syncCount;
137
+ }
138
+ }
139
+ else if (data && typeof data === 'object') {
140
+ if ('primaryKey' in data)
141
+ primaryKeyCount++;
142
+ if ('sync' in data)
143
+ syncCount++;
144
+ // Check related entities
145
+ if (data.relatedEntities) {
146
+ for (const entityData of Object.values(data.relatedEntities)) {
147
+ const stats = this.countSections(entityData);
148
+ primaryKeyCount += stats.primaryKeyCount;
149
+ syncCount += stats.syncCount;
150
+ }
151
+ }
152
+ }
153
+ return { primaryKeyCount, syncCount };
154
+ }
155
+ removeSections(data, sections) {
156
+ if (Array.isArray(data)) {
157
+ return data.map(item => this.removeSections(item, sections));
158
+ }
159
+ else if (data && typeof data === 'object') {
160
+ const cleaned = { ...data };
161
+ // Remove specified sections
162
+ if (sections === 'both' || sections === 'primaryKey') {
163
+ delete cleaned.primaryKey;
164
+ }
165
+ if (sections === 'both' || sections === 'sync') {
166
+ delete cleaned.sync;
167
+ }
168
+ // Process related entities
169
+ if (cleaned.relatedEntities) {
170
+ const cleanedRelated = {};
171
+ for (const [entityName, entityData] of Object.entries(cleaned.relatedEntities)) {
172
+ cleanedRelated[entityName] = this.removeSections(entityData, sections);
173
+ }
174
+ cleaned.relatedEntities = cleanedRelated;
175
+ }
176
+ return cleaned;
177
+ }
178
+ return data;
179
+ }
180
+ }
181
+ exports.FileResetService = FileResetService;
182
+ //# sourceMappingURL=FileResetService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileResetService.js","sourceRoot":"","sources":["../../src/services/FileResetService.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs-extra';\nimport path from 'path';\nimport fastGlob from 'fast-glob';\nimport { loadSyncConfig } from '../config';\nimport { configManager } from '../lib/config-manager';\n\nexport interface FileResetOptions {\n sections?: 'both' | 'primaryKey' | 'sync';\n dryRun?: boolean;\n noBackup?: boolean;\n verbose?: boolean;\n}\n\nexport interface FileResetCallbacks {\n onProgress?: (message: string) => void;\n onLog?: (message: string) => void;\n onWarn?: (message: string) => void;\n onConfirm?: (message: string) => Promise<boolean>;\n}\n\nexport interface FileResetResult {\n processedFiles: number;\n modifiedFiles: number;\n totalPrimaryKeys: number;\n totalSyncs: number;\n filesWithPrimaryKey: number;\n filesWithSync: number;\n backupsCreated: number;\n}\n\nexport interface FileStats {\n primaryKeyCount: number;\n syncCount: number;\n}\n\nexport class FileResetService {\n \n async resetFiles(options: FileResetOptions = {}, callbacks?: FileResetCallbacks): Promise<FileResetResult> {\n const sections = options.sections || 'both';\n \n // Load sync config\n const syncConfig = await loadSyncConfig(configManager.getOriginalCwd());\n if (!syncConfig) {\n throw new Error('No .mj-sync.json found in current directory');\n }\n \n // Find all metadata JSON files\n callbacks?.onProgress?.('Finding metadata files');\n const pattern = syncConfig.filePattern || '.*.json';\n const files = await fastGlob(pattern, {\n cwd: configManager.getOriginalCwd(),\n absolute: true,\n ignore: ['.mj-sync.json', '.mj-folder.json'],\n });\n \n if (files.length === 0) {\n callbacks?.onLog?.('No metadata files found');\n return {\n processedFiles: 0,\n modifiedFiles: 0,\n totalPrimaryKeys: 0,\n totalSyncs: 0,\n filesWithPrimaryKey: 0,\n filesWithSync: 0,\n backupsCreated: 0\n };\n }\n \n callbacks?.onLog?.(`Found ${files.length} metadata file${files.length === 1 ? '' : 's'}`);\n \n // Count what will be removed\n let filesWithPrimaryKey = 0;\n let filesWithSync = 0;\n let totalPrimaryKeys = 0;\n let totalSyncs = 0;\n \n for (const file of files) {\n const content = await fs.readJson(file);\n const stats = this.countSections(content);\n if (stats.primaryKeyCount > 0) {\n filesWithPrimaryKey++;\n totalPrimaryKeys += stats.primaryKeyCount;\n }\n if (stats.syncCount > 0) {\n filesWithSync++;\n totalSyncs += stats.syncCount;\n }\n }\n \n // Report what will be removed\n if (sections === 'both' || sections === 'primaryKey') {\n callbacks?.onLog?.(`Will remove ${totalPrimaryKeys} primaryKey section${totalPrimaryKeys === 1 ? '' : 's'} from ${filesWithPrimaryKey} file${filesWithPrimaryKey === 1 ? '' : 's'}`);\n }\n if (sections === 'both' || sections === 'sync') {\n callbacks?.onLog?.(`Will remove ${totalSyncs} sync section${totalSyncs === 1 ? '' : 's'} from ${filesWithSync} file${filesWithSync === 1 ? '' : 's'}`);\n }\n \n if (options.dryRun) {\n callbacks?.onLog?.('Dry run mode - no files will be modified');\n \n if (options.verbose) {\n for (const file of files) {\n const content = await fs.readJson(file);\n const stats = this.countSections(content);\n if (stats.primaryKeyCount > 0 || stats.syncCount > 0) {\n callbacks?.onLog?.(`${path.relative(configManager.getOriginalCwd(), file)}:`);\n if (stats.primaryKeyCount > 0) {\n callbacks?.onLog?.(` - ${stats.primaryKeyCount} primaryKey section${stats.primaryKeyCount === 1 ? '' : 's'}`);\n }\n if (stats.syncCount > 0) {\n callbacks?.onLog?.(` - ${stats.syncCount} sync section${stats.syncCount === 1 ? '' : 's'}`);\n }\n }\n }\n }\n \n return {\n processedFiles: files.length,\n modifiedFiles: 0,\n totalPrimaryKeys,\n totalSyncs,\n filesWithPrimaryKey,\n filesWithSync,\n backupsCreated: 0\n };\n }\n \n // Process files\n callbacks?.onProgress?.('Processing files');\n let processedFiles = 0;\n let modifiedFiles = 0;\n let backupsCreated = 0;\n \n for (const file of files) {\n processedFiles++;\n const content = await fs.readJson(file);\n const originalContent = JSON.stringify(content);\n \n // Remove sections\n const cleanedContent = this.removeSections(content, sections);\n \n // Only write if content changed\n if (JSON.stringify(cleanedContent) !== originalContent) {\n // Create backup if requested\n if (!options.noBackup) {\n const backupPath = `${file}.backup`;\n await fs.writeJson(backupPath, content, { spaces: 2 });\n backupsCreated++;\n }\n \n // Write cleaned content\n await fs.writeJson(file, cleanedContent, { spaces: 2 });\n modifiedFiles++;\n \n if (options.verbose) {\n callbacks?.onLog?.(`✓ ${path.relative(configManager.getOriginalCwd(), file)}`);\n }\n }\n }\n \n return {\n processedFiles,\n modifiedFiles,\n totalPrimaryKeys,\n totalSyncs,\n filesWithPrimaryKey,\n filesWithSync,\n backupsCreated\n };\n }\n \n private countSections(data: any): FileStats {\n let primaryKeyCount = 0;\n let syncCount = 0;\n \n if (Array.isArray(data)) {\n for (const item of data) {\n const stats = this.countSections(item);\n primaryKeyCount += stats.primaryKeyCount;\n syncCount += stats.syncCount;\n }\n } else if (data && typeof data === 'object') {\n if ('primaryKey' in data) primaryKeyCount++;\n if ('sync' in data) syncCount++;\n \n // Check related entities\n if (data.relatedEntities) {\n for (const entityData of Object.values(data.relatedEntities)) {\n const stats = this.countSections(entityData);\n primaryKeyCount += stats.primaryKeyCount;\n syncCount += stats.syncCount;\n }\n }\n }\n \n return { primaryKeyCount, syncCount };\n }\n \n private removeSections(data: any, sections: string): any {\n if (Array.isArray(data)) {\n return data.map(item => this.removeSections(item, sections));\n } else if (data && typeof data === 'object') {\n const cleaned = { ...data };\n \n // Remove specified sections\n if (sections === 'both' || sections === 'primaryKey') {\n delete cleaned.primaryKey;\n }\n if (sections === 'both' || sections === 'sync') {\n delete cleaned.sync;\n }\n \n // Process related entities\n if (cleaned.relatedEntities) {\n const cleanedRelated: any = {};\n for (const [entityName, entityData] of Object.entries(cleaned.relatedEntities)) {\n cleanedRelated[entityName] = this.removeSections(entityData, sections);\n }\n cleaned.relatedEntities = cleanedRelated;\n }\n \n return cleaned;\n }\n \n return data;\n }\n}"]}
@@ -0,0 +1,17 @@
1
+ export interface InitOptions {
2
+ overwrite?: boolean;
3
+ setupEntity?: 'ai-prompts' | 'other' | 'no';
4
+ entityName?: string;
5
+ dirName?: string;
6
+ }
7
+ export interface InitCallbacks {
8
+ onProgress?: (message: string) => void;
9
+ onSuccess?: (message: string) => void;
10
+ onError?: (message: string) => void;
11
+ }
12
+ export declare class InitService {
13
+ initialize(options?: InitOptions, callbacks?: InitCallbacks): Promise<void>;
14
+ private createAIPromptsExample;
15
+ getNextSteps(): string[];
16
+ getErrorHint(error: Error | string): string | null;
17
+ }
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InitService = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ class InitService {
10
+ async initialize(options = {}, callbacks) {
11
+ try {
12
+ // Check if already initialized
13
+ if (await fs_extra_1.default.pathExists('.mj-sync.json')) {
14
+ if (!options.overwrite) {
15
+ throw new Error('Directory already initialized. Use overwrite option to proceed.');
16
+ }
17
+ }
18
+ // Create root configuration
19
+ const rootConfig = {
20
+ version: '1.0.0',
21
+ push: {
22
+ validateBeforePush: true,
23
+ requireConfirmation: true
24
+ },
25
+ watch: {
26
+ debounceMs: 1000,
27
+ ignorePatterns: ['*.tmp', '*.bak', '.DS_Store']
28
+ }
29
+ };
30
+ callbacks?.onProgress?.('Creating root configuration');
31
+ await fs_extra_1.default.writeJson('.mj-sync.json', rootConfig, { spaces: 2 });
32
+ callbacks?.onSuccess?.('Created .mj-sync.json');
33
+ // Set up entity directory if requested
34
+ if (options.setupEntity && options.setupEntity !== 'no') {
35
+ const entityName = options.setupEntity === 'ai-prompts'
36
+ ? 'AI Prompts'
37
+ : options.entityName || '';
38
+ const dirName = options.setupEntity === 'ai-prompts'
39
+ ? 'ai-prompts'
40
+ : options.dirName || entityName.toLowerCase().replace(/\s+/g, '-');
41
+ // Create entity directory
42
+ callbacks?.onProgress?.(`Creating ${dirName} directory`);
43
+ await fs_extra_1.default.ensureDir(dirName);
44
+ // Create entity configuration
45
+ const entityConfig = {
46
+ entity: entityName,
47
+ filePattern: '*.json',
48
+ defaults: {}
49
+ };
50
+ await fs_extra_1.default.writeJson(path_1.default.join(dirName, '.mj-sync.json'), entityConfig, { spaces: 2 });
51
+ callbacks?.onSuccess?.(`Created ${dirName} directory with entity configuration`);
52
+ // Create example structure
53
+ if (options.setupEntity === 'ai-prompts') {
54
+ await this.createAIPromptsExample(dirName);
55
+ }
56
+ }
57
+ }
58
+ catch (error) {
59
+ const errorMessage = error instanceof Error ? error.message : String(error);
60
+ callbacks?.onError?.(errorMessage);
61
+ throw error;
62
+ }
63
+ }
64
+ async createAIPromptsExample(dirName) {
65
+ const exampleDir = path_1.default.join(dirName, 'examples');
66
+ await fs_extra_1.default.ensureDir(exampleDir);
67
+ // Create folder config
68
+ const folderConfig = {
69
+ defaults: {
70
+ CategoryID: '@lookup:AI Prompt Categories.Name=Examples',
71
+ Temperature: 0.7
72
+ }
73
+ };
74
+ await fs_extra_1.default.writeJson(path_1.default.join(exampleDir, '.mj-folder.json'), folderConfig, { spaces: 2 });
75
+ // Create example prompt
76
+ const examplePrompt = {
77
+ _primaryKey: {
78
+ ID: 'example-001'
79
+ },
80
+ _fields: {
81
+ Name: 'Example Greeting Prompt',
82
+ Description: 'A simple example prompt to demonstrate the sync tool',
83
+ PromptTypeID: '@lookup:AI Prompt Types.Name=Chat',
84
+ Temperature: 0.8,
85
+ MaxTokens: 150,
86
+ Prompt: '@file:greeting.prompt.md'
87
+ }
88
+ };
89
+ await fs_extra_1.default.writeJson(path_1.default.join(exampleDir, 'greeting.json'), examplePrompt, { spaces: 2 });
90
+ // Create the markdown file
91
+ const promptContent = `You are a friendly assistant. Please greet the user warmly and ask how you can help them today.
92
+
93
+ Be conversational and welcoming in your tone.`;
94
+ await fs_extra_1.default.writeFile(path_1.default.join(exampleDir, 'greeting.prompt.md'), promptContent);
95
+ }
96
+ getNextSteps() {
97
+ return [
98
+ 'Run "mj sync pull --entity=\'AI Prompts\'" to pull existing data',
99
+ 'Edit files locally',
100
+ 'Run "mj sync push" to sync changes back to the database'
101
+ ];
102
+ }
103
+ getErrorHint(error) {
104
+ const errorMessage = error instanceof Error ? error.message : String(error);
105
+ if (errorMessage.includes('permission') || errorMessage.includes('EACCES')) {
106
+ return 'This appears to be a file permission issue. Make sure you have write permissions in the current directory.';
107
+ }
108
+ else if (errorMessage.includes('ENOENT') || errorMessage.includes('no such file')) {
109
+ return 'This appears to be a file or directory access issue. Make sure the current directory exists and is accessible.';
110
+ }
111
+ else if (errorMessage.includes('already exists') || errorMessage.includes('EEXIST')) {
112
+ return 'Files or directories already exist. Try using the overwrite option or manually remove existing files.';
113
+ }
114
+ return null;
115
+ }
116
+ }
117
+ exports.InitService = InitService;
118
+ //# sourceMappingURL=InitService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InitService.js","sourceRoot":"","sources":["../../src/services/InitService.ts"],"names":[],"mappings":";;;;;;AAAA,wDAA0B;AAC1B,gDAAwB;AAexB,MAAa,WAAW;IACtB,KAAK,CAAC,UAAU,CAAC,UAAuB,EAAE,EAAE,SAAyB;QACnE,IAAI,CAAC;YACH,+BAA+B;YAC/B,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;gBACzC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;oBACvB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;gBACrF,CAAC;YACH,CAAC;YAED,4BAA4B;YAC5B,MAAM,UAAU,GAAG;gBACjB,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE;oBACJ,kBAAkB,EAAE,IAAI;oBACxB,mBAAmB,EAAE,IAAI;iBAC1B;gBACD,KAAK,EAAE;oBACL,UAAU,EAAE,IAAI;oBAChB,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC;iBAChD;aACF,CAAC;YAEF,SAAS,EAAE,UAAU,EAAE,CAAC,6BAA6B,CAAC,CAAC;YACvD,MAAM,kBAAE,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAC/D,SAAS,EAAE,SAAS,EAAE,CAAC,uBAAuB,CAAC,CAAC;YAEhD,uCAAuC;YACvC,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;gBACxD,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,KAAK,YAAY;oBACrD,CAAC,CAAC,YAAY;oBACd,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;gBAE7B,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,KAAK,YAAY;oBAClD,CAAC,CAAC,YAAY;oBACd,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;gBAErE,0BAA0B;gBAC1B,SAAS,EAAE,UAAU,EAAE,CAAC,YAAY,OAAO,YAAY,CAAC,CAAC;gBACzD,MAAM,kBAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBAE5B,8BAA8B;gBAC9B,MAAM,YAAY,GAAG;oBACnB,MAAM,EAAE,UAAU;oBAClB,WAAW,EAAE,QAAQ;oBACrB,QAAQ,EAAE,EAAE;iBACb,CAAC;gBAEF,MAAM,kBAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;gBACrF,SAAS,EAAE,SAAS,EAAE,CAAC,WAAW,OAAO,sCAAsC,CAAC,CAAC;gBAEjF,2BAA2B;gBAC3B,IAAI,OAAO,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;oBACzC,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC;QAEH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,SAAS,EAAE,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC;YACnC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAAC,OAAe;QAClD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAClD,MAAM,kBAAE,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAE/B,uBAAuB;QACvB,MAAM,YAAY,GAAG;YACnB,QAAQ,EAAE;gBACR,UAAU,EAAE,4CAA4C;gBACxD,WAAW,EAAE,GAAG;aACjB;SACF,CAAC;QAEF,MAAM,kBAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QAE1F,wBAAwB;QACxB,MAAM,aAAa,GAAG;YACpB,WAAW,EAAE;gBACX,EAAE,EAAE,aAAa;aAClB;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,yBAAyB;gBAC/B,WAAW,EAAE,sDAAsD;gBACnE,YAAY,EAAE,mCAAmC;gBACjD,WAAW,EAAE,GAAG;gBAChB,SAAS,EAAE,GAAG;gBACd,MAAM,EAAE,0BAA0B;aACnC;SACF,CAAC;QAEF,MAAM,kBAAE,CAAC,SAAS,CAChB,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,EACtC,aAAa,EACb,EAAE,MAAM,EAAE,CAAC,EAAE,CACd,CAAC;QAEF,2BAA2B;QAC3B,MAAM,aAAa,GAAG;;8CAEoB,CAAC;QAE3C,MAAM,kBAAE,CAAC,SAAS,CAChB,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,oBAAoB,CAAC,EAC3C,aAAa,CACd,CAAC;IACJ,CAAC;IAED,YAAY;QACV,OAAO;YACL,kEAAkE;YAClE,oBAAoB;YACpB,yDAAyD;SAC1D,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,KAAqB;QAChC,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE5E,IAAI,YAAY,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3E,OAAO,4GAA4G,CAAC;QACtH,CAAC;aAAM,IAAI,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACpF,OAAO,gHAAgH,CAAC;QAC1H,CAAC;aAAM,IAAI,YAAY,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtF,OAAO,uGAAuG,CAAC;QACjH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAnID,kCAmIC","sourcesContent":["import fs from 'fs-extra';\nimport path from 'path';\n\nexport interface InitOptions {\n overwrite?: boolean;\n setupEntity?: 'ai-prompts' | 'other' | 'no';\n entityName?: string;\n dirName?: string;\n}\n\nexport interface InitCallbacks {\n onProgress?: (message: string) => void;\n onSuccess?: (message: string) => void;\n onError?: (message: string) => void;\n}\n\nexport class InitService {\n async initialize(options: InitOptions = {}, callbacks?: InitCallbacks): Promise<void> {\n try {\n // Check if already initialized\n if (await fs.pathExists('.mj-sync.json')) {\n if (!options.overwrite) {\n throw new Error('Directory already initialized. Use overwrite option to proceed.');\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 callbacks?.onProgress?.('Creating root configuration');\n await fs.writeJson('.mj-sync.json', rootConfig, { spaces: 2 });\n callbacks?.onSuccess?.('Created .mj-sync.json');\n \n // Set up entity directory if requested\n if (options.setupEntity && options.setupEntity !== 'no') {\n const entityName = options.setupEntity === 'ai-prompts' \n ? 'AI Prompts'\n : options.entityName || '';\n \n const dirName = options.setupEntity === 'ai-prompts'\n ? 'ai-prompts'\n : options.dirName || entityName.toLowerCase().replace(/\\s+/g, '-');\n \n // Create entity directory\n callbacks?.onProgress?.(`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 callbacks?.onSuccess?.(`Created ${dirName} directory with entity configuration`);\n \n // Create example structure\n if (options.setupEntity === 'ai-prompts') {\n await this.createAIPromptsExample(dirName);\n }\n }\n \n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n callbacks?.onError?.(errorMessage);\n throw 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 \n getNextSteps(): string[] {\n return [\n 'Run \"mj sync pull --entity=\\'AI Prompts\\'\" to pull existing data',\n 'Edit files locally',\n 'Run \"mj sync push\" to sync changes back to the database'\n ];\n }\n \n getErrorHint(error: Error | string): string | null {\n const errorMessage = error instanceof Error ? error.message : String(error);\n \n if (errorMessage.includes('permission') || errorMessage.includes('EACCES')) {\n return 'This appears to be a file permission issue. Make sure you have write permissions in the current directory.';\n } else if (errorMessage.includes('ENOENT') || errorMessage.includes('no such file')) {\n return 'This appears to be a file or directory access issue. Make sure the current directory exists and is accessible.';\n } else if (errorMessage.includes('already exists') || errorMessage.includes('EEXIST')) {\n return 'Files or directories already exist. Try using the overwrite option or manually remove existing files.';\n }\n \n return null;\n }\n}"]}
@@ -0,0 +1,45 @@
1
+ import { UserInfo } from '@memberjunction/core';
2
+ import { SyncEngine } from '../lib/sync-engine';
3
+ export interface PullOptions {
4
+ entity: string;
5
+ filter?: string;
6
+ dryRun?: boolean;
7
+ multiFile?: string;
8
+ verbose?: boolean;
9
+ noValidate?: boolean;
10
+ targetDir?: string;
11
+ updateExistingRecords?: boolean;
12
+ createNewFileIfNotFound?: boolean;
13
+ }
14
+ export interface PullCallbacks {
15
+ onProgress?: (message: string) => void;
16
+ onSuccess?: (message: string) => void;
17
+ onError?: (message: string) => void;
18
+ onWarn?: (message: string) => void;
19
+ onLog?: (message: string) => void;
20
+ }
21
+ export interface PullResult {
22
+ processed: number;
23
+ created: number;
24
+ updated: number;
25
+ skipped: number;
26
+ targetDir: string;
27
+ }
28
+ export declare class PullService {
29
+ private syncEngine;
30
+ private contextUser;
31
+ constructor(syncEngine: SyncEngine, contextUser: UserInfo);
32
+ pull(options: PullOptions, callbacks?: PullCallbacks): Promise<PullResult>;
33
+ private handleAsyncPropertyLoading;
34
+ private processRecords;
35
+ private processIndividualRecords;
36
+ private processRecord;
37
+ private processRecordData;
38
+ private findEntityDirectories;
39
+ private buildFileName;
40
+ private findExistingFiles;
41
+ private loadExistingRecords;
42
+ private createPrimaryKeyLookup;
43
+ private mergeRecords;
44
+ private createBackup;
45
+ }