@memberjunction/metadata-sync 2.55.0 → 2.57.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 (67) hide show
  1. package/README.md +92 -51
  2. package/dist/index.d.ts +21 -1
  3. package/dist/index.js +43 -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/provider-utils.d.ts +2 -2
  8. package/dist/lib/provider-utils.js.map +1 -1
  9. package/dist/lib/sql-logger.d.ts +44 -0
  10. package/dist/lib/sql-logger.js +140 -0
  11. package/dist/lib/sql-logger.js.map +1 -0
  12. package/dist/lib/sync-engine.d.ts +25 -0
  13. package/dist/lib/sync-engine.js +72 -2
  14. package/dist/lib/sync-engine.js.map +1 -1
  15. package/dist/lib/transaction-manager.d.ts +35 -0
  16. package/dist/lib/transaction-manager.js +100 -0
  17. package/dist/lib/transaction-manager.js.map +1 -0
  18. package/dist/services/FileResetService.d.ts +30 -0
  19. package/dist/services/FileResetService.js +183 -0
  20. package/dist/services/FileResetService.js.map +1 -0
  21. package/dist/services/InitService.d.ts +17 -0
  22. package/dist/services/InitService.js +118 -0
  23. package/dist/services/InitService.js.map +1 -0
  24. package/dist/services/PullService.d.ts +45 -0
  25. package/dist/services/PullService.js +564 -0
  26. package/dist/services/PullService.js.map +1 -0
  27. package/dist/services/PushService.d.ts +47 -0
  28. package/dist/services/PushService.js +558 -0
  29. package/dist/services/PushService.js.map +1 -0
  30. package/dist/services/StatusService.d.ts +32 -0
  31. package/dist/services/StatusService.js +138 -0
  32. package/dist/services/StatusService.js.map +1 -0
  33. package/dist/services/WatchService.d.ts +34 -0
  34. package/dist/services/WatchService.js +296 -0
  35. package/dist/services/WatchService.js.map +1 -0
  36. package/dist/services/index.d.ts +16 -0
  37. package/dist/services/index.js +28 -0
  38. package/dist/services/index.js.map +1 -0
  39. package/package.json +14 -45
  40. package/bin/debug.js +0 -7
  41. package/bin/run +0 -17
  42. package/bin/run.js +0 -6
  43. package/dist/commands/file-reset/index.d.ts +0 -15
  44. package/dist/commands/file-reset/index.js +0 -221
  45. package/dist/commands/file-reset/index.js.map +0 -1
  46. package/dist/commands/init/index.d.ts +0 -7
  47. package/dist/commands/init/index.js +0 -155
  48. package/dist/commands/init/index.js.map +0 -1
  49. package/dist/commands/pull/index.d.ts +0 -246
  50. package/dist/commands/pull/index.js +0 -1448
  51. package/dist/commands/pull/index.js.map +0 -1
  52. package/dist/commands/push/index.d.ts +0 -41
  53. package/dist/commands/push/index.js +0 -1129
  54. package/dist/commands/push/index.js.map +0 -1
  55. package/dist/commands/status/index.d.ts +0 -10
  56. package/dist/commands/status/index.js +0 -199
  57. package/dist/commands/status/index.js.map +0 -1
  58. package/dist/commands/validate/index.d.ts +0 -15
  59. package/dist/commands/validate/index.js +0 -149
  60. package/dist/commands/validate/index.js.map +0 -1
  61. package/dist/commands/watch/index.d.ts +0 -15
  62. package/dist/commands/watch/index.js +0 -300
  63. package/dist/commands/watch/index.js.map +0 -1
  64. package/dist/hooks/init.d.ts +0 -3
  65. package/dist/hooks/init.js +0 -59
  66. package/dist/hooks/init.js.map +0 -1
  67. package/oclif.manifest.json +0 -376
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Transaction Manager for atomic database operations
4
+ * @module transaction-manager
5
+ *
6
+ * This module provides transaction support for push operations to ensure
7
+ * all related entities are saved atomically with rollback capability.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.TransactionManager = void 0;
11
+ const provider_utils_1 = require("./provider-utils");
12
+ class TransactionManager {
13
+ inTransaction = false;
14
+ sqlLogger;
15
+ constructor(sqlLogger) {
16
+ this.sqlLogger = sqlLogger;
17
+ }
18
+ /**
19
+ * Begin a new transaction
20
+ */
21
+ async beginTransaction(options) {
22
+ if (this.inTransaction) {
23
+ throw new Error('Transaction already in progress');
24
+ }
25
+ const provider = (0, provider_utils_1.getDataProvider)();
26
+ if (!provider) {
27
+ throw new Error('No data provider available');
28
+ }
29
+ // Check if provider supports transactions (PascalCase method names)
30
+ try {
31
+ await provider.BeginTransaction();
32
+ this.inTransaction = true;
33
+ }
34
+ catch (error) {
35
+ throw new Error(`Failed to begin transaction: ${error instanceof Error ? error.message : String(error)}`);
36
+ }
37
+ }
38
+ /**
39
+ * Commit the current transaction
40
+ */
41
+ async commitTransaction() {
42
+ if (!this.inTransaction) {
43
+ return; // No transaction to commit
44
+ }
45
+ const provider = (0, provider_utils_1.getDataProvider)();
46
+ if (!provider) {
47
+ throw new Error('No data provider available');
48
+ }
49
+ try {
50
+ await provider.CommitTransaction();
51
+ this.inTransaction = false;
52
+ }
53
+ catch (error) {
54
+ throw new Error(`Failed to commit transaction: ${error instanceof Error ? error.message : String(error)}`);
55
+ }
56
+ }
57
+ /**
58
+ * Rollback the current transaction
59
+ */
60
+ async rollbackTransaction() {
61
+ if (!this.inTransaction) {
62
+ return; // No transaction to rollback
63
+ }
64
+ const provider = (0, provider_utils_1.getDataProvider)();
65
+ if (!provider) {
66
+ throw new Error('No data provider available');
67
+ }
68
+ try {
69
+ await provider.RollbackTransaction();
70
+ this.inTransaction = false;
71
+ }
72
+ catch (error) {
73
+ // Log but don't throw - we're already in an error state
74
+ console.error('Failed to rollback transaction:', error);
75
+ }
76
+ }
77
+ /**
78
+ * Execute a function within a transaction
79
+ */
80
+ async executeInTransaction(fn, options) {
81
+ await this.beginTransaction(options);
82
+ try {
83
+ const result = await fn();
84
+ await this.commitTransaction();
85
+ return result;
86
+ }
87
+ catch (error) {
88
+ await this.rollbackTransaction();
89
+ throw error;
90
+ }
91
+ }
92
+ /**
93
+ * Check if currently in a transaction
94
+ */
95
+ get isInTransaction() {
96
+ return this.inTransaction;
97
+ }
98
+ }
99
+ exports.TransactionManager = TransactionManager;
100
+ //# sourceMappingURL=transaction-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction-manager.js","sourceRoot":"","sources":["../../src/lib/transaction-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Transaction Manager for atomic database operations\n * @module transaction-manager\n * \n * This module provides transaction support for push operations to ensure\n * all related entities are saved atomically with rollback capability.\n */\n\nimport { getDataProvider } from './provider-utils';\nimport { SQLLogger } from './sql-logger';\n\nexport interface TransactionOptions {\n}\n\nexport class TransactionManager {\n private inTransaction = false;\n private sqlLogger?: SQLLogger;\n \n constructor(sqlLogger?: SQLLogger) {\n this.sqlLogger = sqlLogger;\n }\n \n /**\n * Begin a new transaction\n */\n async beginTransaction(options?: TransactionOptions): Promise<void> {\n if (this.inTransaction) {\n throw new Error('Transaction already in progress');\n }\n \n const provider = getDataProvider();\n if (!provider) {\n throw new Error('No data provider available');\n }\n \n // Check if provider supports transactions (PascalCase method names)\n try {\n await provider.BeginTransaction();\n this.inTransaction = true;\n } catch (error) {\n throw new Error(`Failed to begin transaction: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n \n /**\n * Commit the current transaction\n */\n async commitTransaction(): Promise<void> {\n if (!this.inTransaction) {\n return; // No transaction to commit\n }\n \n const provider = getDataProvider();\n if (!provider) {\n throw new Error('No data provider available');\n }\n \n try {\n await provider.CommitTransaction();\n this.inTransaction = false;\n } catch (error) {\n throw new Error(`Failed to commit transaction: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n \n /**\n * Rollback the current transaction\n */\n async rollbackTransaction(): Promise<void> {\n if (!this.inTransaction) {\n return; // No transaction to rollback\n }\n \n const provider = getDataProvider();\n if (!provider) {\n throw new Error('No data provider available');\n }\n \n try {\n await provider.RollbackTransaction();\n this.inTransaction = false;\n } catch (error) {\n // Log but don't throw - we're already in an error state\n console.error('Failed to rollback transaction:', error);\n }\n }\n \n /**\n * Execute a function within a transaction\n */\n async executeInTransaction<T>(\n fn: () => Promise<T>,\n options?: TransactionOptions\n ): Promise<T> {\n await this.beginTransaction(options);\n \n try {\n const result = await fn();\n await this.commitTransaction();\n return result;\n } catch (error) {\n await this.rollbackTransaction();\n throw error;\n }\n }\n \n /**\n * Check if currently in a transaction\n */\n get isInTransaction(): boolean {\n return this.inTransaction;\n }\n}"]}
@@ -0,0 +1,30 @@
1
+ export interface FileResetOptions {
2
+ sections?: 'both' | 'primaryKey' | 'sync';
3
+ dryRun?: boolean;
4
+ noBackup?: boolean;
5
+ verbose?: boolean;
6
+ }
7
+ export interface FileResetCallbacks {
8
+ onProgress?: (message: string) => void;
9
+ onLog?: (message: string) => void;
10
+ onWarn?: (message: string) => void;
11
+ onConfirm?: (message: string) => Promise<boolean>;
12
+ }
13
+ export interface FileResetResult {
14
+ processedFiles: number;
15
+ modifiedFiles: number;
16
+ totalPrimaryKeys: number;
17
+ totalSyncs: number;
18
+ filesWithPrimaryKey: number;
19
+ filesWithSync: number;
20
+ backupsCreated: number;
21
+ }
22
+ export interface FileStats {
23
+ primaryKeyCount: number;
24
+ syncCount: number;
25
+ }
26
+ export declare class FileResetService {
27
+ resetFiles(options?: FileResetOptions, callbacks?: FileResetCallbacks): Promise<FileResetResult>;
28
+ private countSections;
29
+ private removeSections;
30
+ }
@@ -0,0 +1,183 @@
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
+ dot: true,
27
+ ignore: ['.mj-sync.json', '.mj-folder.json'],
28
+ });
29
+ if (files.length === 0) {
30
+ callbacks?.onLog?.('No metadata files found');
31
+ return {
32
+ processedFiles: 0,
33
+ modifiedFiles: 0,
34
+ totalPrimaryKeys: 0,
35
+ totalSyncs: 0,
36
+ filesWithPrimaryKey: 0,
37
+ filesWithSync: 0,
38
+ backupsCreated: 0
39
+ };
40
+ }
41
+ callbacks?.onLog?.(`Found ${files.length} metadata file${files.length === 1 ? '' : 's'}`);
42
+ // Count what will be removed
43
+ let filesWithPrimaryKey = 0;
44
+ let filesWithSync = 0;
45
+ let totalPrimaryKeys = 0;
46
+ let totalSyncs = 0;
47
+ for (const file of files) {
48
+ const content = await fs_extra_1.default.readJson(file);
49
+ const stats = this.countSections(content);
50
+ if (stats.primaryKeyCount > 0) {
51
+ filesWithPrimaryKey++;
52
+ totalPrimaryKeys += stats.primaryKeyCount;
53
+ }
54
+ if (stats.syncCount > 0) {
55
+ filesWithSync++;
56
+ totalSyncs += stats.syncCount;
57
+ }
58
+ }
59
+ // Report what will be removed
60
+ if (sections === 'both' || sections === 'primaryKey') {
61
+ callbacks?.onLog?.(`Will remove ${totalPrimaryKeys} primaryKey section${totalPrimaryKeys === 1 ? '' : 's'} from ${filesWithPrimaryKey} file${filesWithPrimaryKey === 1 ? '' : 's'}`);
62
+ }
63
+ if (sections === 'both' || sections === 'sync') {
64
+ callbacks?.onLog?.(`Will remove ${totalSyncs} sync section${totalSyncs === 1 ? '' : 's'} from ${filesWithSync} file${filesWithSync === 1 ? '' : 's'}`);
65
+ }
66
+ if (options.dryRun) {
67
+ callbacks?.onLog?.('Dry run mode - no files will be modified');
68
+ if (options.verbose) {
69
+ for (const file of files) {
70
+ const content = await fs_extra_1.default.readJson(file);
71
+ const stats = this.countSections(content);
72
+ if (stats.primaryKeyCount > 0 || stats.syncCount > 0) {
73
+ callbacks?.onLog?.(`${path_1.default.relative(config_manager_1.configManager.getOriginalCwd(), file)}:`);
74
+ if (stats.primaryKeyCount > 0) {
75
+ callbacks?.onLog?.(` - ${stats.primaryKeyCount} primaryKey section${stats.primaryKeyCount === 1 ? '' : 's'}`);
76
+ }
77
+ if (stats.syncCount > 0) {
78
+ callbacks?.onLog?.(` - ${stats.syncCount} sync section${stats.syncCount === 1 ? '' : 's'}`);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ return {
84
+ processedFiles: files.length,
85
+ modifiedFiles: 0,
86
+ totalPrimaryKeys,
87
+ totalSyncs,
88
+ filesWithPrimaryKey,
89
+ filesWithSync,
90
+ backupsCreated: 0
91
+ };
92
+ }
93
+ // Process files
94
+ callbacks?.onProgress?.('Processing files');
95
+ let processedFiles = 0;
96
+ let modifiedFiles = 0;
97
+ let backupsCreated = 0;
98
+ for (const file of files) {
99
+ processedFiles++;
100
+ const content = await fs_extra_1.default.readJson(file);
101
+ const originalContent = JSON.stringify(content);
102
+ // Remove sections
103
+ const cleanedContent = this.removeSections(content, sections);
104
+ // Only write if content changed
105
+ if (JSON.stringify(cleanedContent) !== originalContent) {
106
+ // Create backup if requested
107
+ if (!options.noBackup) {
108
+ const backupPath = `${file}.backup`;
109
+ await fs_extra_1.default.writeJson(backupPath, content, { spaces: 2 });
110
+ backupsCreated++;
111
+ }
112
+ // Write cleaned content
113
+ await fs_extra_1.default.writeJson(file, cleanedContent, { spaces: 2 });
114
+ modifiedFiles++;
115
+ if (options.verbose) {
116
+ callbacks?.onLog?.(`✓ ${path_1.default.relative(config_manager_1.configManager.getOriginalCwd(), file)}`);
117
+ }
118
+ }
119
+ }
120
+ return {
121
+ processedFiles,
122
+ modifiedFiles,
123
+ totalPrimaryKeys,
124
+ totalSyncs,
125
+ filesWithPrimaryKey,
126
+ filesWithSync,
127
+ backupsCreated
128
+ };
129
+ }
130
+ countSections(data) {
131
+ let primaryKeyCount = 0;
132
+ let syncCount = 0;
133
+ if (Array.isArray(data)) {
134
+ for (const item of data) {
135
+ const stats = this.countSections(item);
136
+ primaryKeyCount += stats.primaryKeyCount;
137
+ syncCount += stats.syncCount;
138
+ }
139
+ }
140
+ else if (data && typeof data === 'object') {
141
+ if ('primaryKey' in data)
142
+ primaryKeyCount++;
143
+ if ('sync' in data)
144
+ syncCount++;
145
+ // Check related entities
146
+ if (data.relatedEntities) {
147
+ for (const entityData of Object.values(data.relatedEntities)) {
148
+ const stats = this.countSections(entityData);
149
+ primaryKeyCount += stats.primaryKeyCount;
150
+ syncCount += stats.syncCount;
151
+ }
152
+ }
153
+ }
154
+ return { primaryKeyCount, syncCount };
155
+ }
156
+ removeSections(data, sections) {
157
+ if (Array.isArray(data)) {
158
+ return data.map(item => this.removeSections(item, sections));
159
+ }
160
+ else if (data && typeof data === 'object') {
161
+ const cleaned = { ...data };
162
+ // Remove specified sections
163
+ if (sections === 'both' || sections === 'primaryKey') {
164
+ delete cleaned.primaryKey;
165
+ }
166
+ if (sections === 'both' || sections === 'sync') {
167
+ delete cleaned.sync;
168
+ }
169
+ // Process related entities
170
+ if (cleaned.relatedEntities) {
171
+ const cleanedRelated = {};
172
+ for (const [entityName, entityData] of Object.entries(cleaned.relatedEntities)) {
173
+ cleanedRelated[entityName] = this.removeSections(entityData, sections);
174
+ }
175
+ cleaned.relatedEntities = cleanedRelated;
176
+ }
177
+ return cleaned;
178
+ }
179
+ return data;
180
+ }
181
+ }
182
+ exports.FileResetService = FileResetService;
183
+ //# 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 dot: 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":"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","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
+ }