@memberjunction/metadata-sync 2.56.0 → 2.58.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.
@@ -26,15 +26,10 @@ class TransactionManager {
26
26
  if (!provider) {
27
27
  throw new Error('No data provider available');
28
28
  }
29
- // Check if provider supports transactions
30
- if (typeof provider.beginTransaction !== 'function') {
31
- // Provider doesn't support transactions, operate without them
32
- return;
33
- }
29
+ // Check if provider supports transactions (PascalCase method names)
34
30
  try {
35
- await provider.beginTransaction(options?.isolationLevel);
31
+ await provider.BeginTransaction();
36
32
  this.inTransaction = true;
37
- this.sqlLogger?.logTransaction('BEGIN');
38
33
  }
39
34
  catch (error) {
40
35
  throw new Error(`Failed to begin transaction: ${error instanceof Error ? error.message : String(error)}`);
@@ -51,15 +46,9 @@ class TransactionManager {
51
46
  if (!provider) {
52
47
  throw new Error('No data provider available');
53
48
  }
54
- // Check if provider supports transactions
55
- if (typeof provider.commitTransaction !== 'function') {
56
- this.inTransaction = false;
57
- return;
58
- }
59
49
  try {
60
- await provider.commitTransaction();
50
+ await provider.CommitTransaction();
61
51
  this.inTransaction = false;
62
- this.sqlLogger?.logTransaction('COMMIT');
63
52
  }
64
53
  catch (error) {
65
54
  throw new Error(`Failed to commit transaction: ${error instanceof Error ? error.message : String(error)}`);
@@ -76,15 +65,9 @@ class TransactionManager {
76
65
  if (!provider) {
77
66
  throw new Error('No data provider available');
78
67
  }
79
- // Check if provider supports transactions
80
- if (typeof provider.rollbackTransaction !== 'function') {
81
- this.inTransaction = false;
82
- return;
83
- }
84
68
  try {
85
- await provider.rollbackTransaction();
69
+ await provider.RollbackTransaction();
86
70
  this.inTransaction = false;
87
- this.sqlLogger?.logTransaction('ROLLBACK');
88
71
  }
89
72
  catch (error) {
90
73
  // Log but don't throw - we're already in an error state
@@ -1 +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 isolationLevel?: 'READ_UNCOMMITTED' | 'READ_COMMITTED' | 'REPEATABLE_READ' | 'SERIALIZABLE';\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\n if (typeof (provider as any).beginTransaction !== 'function') {\n // Provider doesn't support transactions, operate without them\n return;\n }\n \n try {\n await (provider as any).beginTransaction(options?.isolationLevel);\n this.inTransaction = true;\n this.sqlLogger?.logTransaction('BEGIN');\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 // Check if provider supports transactions\n if (typeof (provider as any).commitTransaction !== 'function') {\n this.inTransaction = false;\n return;\n }\n \n try {\n await (provider as any).commitTransaction();\n this.inTransaction = false;\n this.sqlLogger?.logTransaction('COMMIT');\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 // Check if provider supports transactions\n if (typeof (provider as any).rollbackTransaction !== 'function') {\n this.inTransaction = false;\n return;\n }\n \n try {\n await (provider as any).rollbackTransaction();\n this.inTransaction = false;\n this.sqlLogger?.logTransaction('ROLLBACK');\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}"]}
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}"]}
@@ -23,6 +23,7 @@ class FileResetService {
23
23
  const files = await (0, fast_glob_1.default)(pattern, {
24
24
  cwd: config_manager_1.configManager.getOriginalCwd(),
25
25
  absolute: true,
26
+ dot: true,
26
27
  ignore: ['.mj-sync.json', '.mj-folder.json'],
27
28
  });
28
29
  if (files.length === 0) {
@@ -1 +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}"]}
1
+ {"version":3,"file":"FileResetService.js","sourceRoot":"","sources":["../../src/services/FileResetService.ts"],"names":[],"mappings":";;;;;;AAAA,wDAA0B;AAC1B,gDAAwB;AACxB,0DAAiC;AACjC,sCAA2C;AAC3C,0DAAsD;AA+BtD,MAAa,gBAAgB;IAE3B,KAAK,CAAC,UAAU,CAAC,UAA4B,EAAE,EAAE,SAA8B;QAC7E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC;QAE5C,mBAAmB;QACnB,MAAM,UAAU,GAAG,MAAM,IAAA,uBAAc,EAAC,8BAAa,CAAC,cAAc,EAAE,CAAC,CAAC;QACxE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QAED,+BAA+B;QAC/B,SAAS,EAAE,UAAU,EAAE,CAAC,wBAAwB,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,IAAI,SAAS,CAAC;QACpD,MAAM,KAAK,GAAG,MAAM,IAAA,mBAAQ,EAAC,OAAO,EAAE;YACpC,GAAG,EAAE,8BAAa,CAAC,cAAc,EAAE;YACnC,QAAQ,EAAE,IAAI;YACd,GAAG,EAAE,IAAI;YACT,MAAM,EAAE,CAAC,eAAe,EAAE,iBAAiB,CAAC;SAC7C,CAAC,CAAC;QAEH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,SAAS,EAAE,KAAK,EAAE,CAAC,yBAAyB,CAAC,CAAC;YAC9C,OAAO;gBACL,cAAc,EAAE,CAAC;gBACjB,aAAa,EAAE,CAAC;gBAChB,gBAAgB,EAAE,CAAC;gBACnB,UAAU,EAAE,CAAC;gBACb,mBAAmB,EAAE,CAAC;gBACtB,aAAa,EAAE,CAAC;gBAChB,cAAc,EAAE,CAAC;aAClB,CAAC;QACJ,CAAC;QAED,SAAS,EAAE,KAAK,EAAE,CAAC,SAAS,KAAK,CAAC,MAAM,iBAAiB,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAE1F,6BAA6B;QAC7B,IAAI,mBAAmB,GAAG,CAAC,CAAC;QAC5B,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,IAAI,gBAAgB,GAAG,CAAC,CAAC;QACzB,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,KAAK,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;gBAC9B,mBAAmB,EAAE,CAAC;gBACtB,gBAAgB,IAAI,KAAK,CAAC,eAAe,CAAC;YAC5C,CAAC;YACD,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;gBACxB,aAAa,EAAE,CAAC;gBAChB,UAAU,IAAI,KAAK,CAAC,SAAS,CAAC;YAChC,CAAC;QACH,CAAC;QAED,8BAA8B;QAC9B,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;YACrD,SAAS,EAAE,KAAK,EAAE,CAAC,eAAe,gBAAgB,sBAAsB,gBAAgB,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,mBAAmB,QAAQ,mBAAmB,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACvL,CAAC;QACD,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;YAC/C,SAAS,EAAE,KAAK,EAAE,CAAC,eAAe,UAAU,gBAAgB,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,aAAa,QAAQ,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACzJ,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,SAAS,EAAE,KAAK,EAAE,CAAC,0CAA0C,CAAC,CAAC;YAE/D,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,MAAM,OAAO,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;oBACxC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;oBAC1C,IAAI,KAAK,CAAC,eAAe,GAAG,CAAC,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;wBACrD,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,cAAI,CAAC,QAAQ,CAAC,8BAAa,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;wBAC9E,IAAI,KAAK,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;4BAC9B,SAAS,EAAE,KAAK,EAAE,CAAC,OAAO,KAAK,CAAC,eAAe,sBAAsB,KAAK,CAAC,eAAe,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;wBACjH,CAAC;wBACD,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;4BACxB,SAAS,EAAE,KAAK,EAAE,CAAC,OAAO,KAAK,CAAC,SAAS,gBAAgB,KAAK,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;wBAC/F,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAED,OAAO;gBACL,cAAc,EAAE,KAAK,CAAC,MAAM;gBAC5B,aAAa,EAAE,CAAC;gBAChB,gBAAgB;gBAChB,UAAU;gBACV,mBAAmB;gBACnB,aAAa;gBACb,cAAc,EAAE,CAAC;aAClB,CAAC;QACJ,CAAC;QAED,gBAAgB;QAChB,SAAS,EAAE,UAAU,EAAE,CAAC,kBAAkB,CAAC,CAAC;QAC5C,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,IAAI,cAAc,GAAG,CAAC,CAAC;QAEvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,cAAc,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAEhD,kBAAkB;YAClB,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAE9D,gCAAgC;YAChC,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,eAAe,EAAE,CAAC;gBACvD,6BAA6B;gBAC7B,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACtB,MAAM,UAAU,GAAG,GAAG,IAAI,SAAS,CAAC;oBACpC,MAAM,kBAAE,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;oBACvD,cAAc,EAAE,CAAC;gBACnB,CAAC;gBAED,wBAAwB;gBACxB,MAAM,kBAAE,CAAC,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;gBACxD,aAAa,EAAE,CAAC;gBAEhB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACpB,SAAS,EAAE,KAAK,EAAE,CAAC,KAAK,cAAI,CAAC,QAAQ,CAAC,8BAAa,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;gBACjF,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,cAAc;YACd,aAAa;YACb,gBAAgB;YAChB,UAAU;YACV,mBAAmB;YACnB,aAAa;YACb,cAAc;SACf,CAAC;IACJ,CAAC;IAEO,aAAa,CAAC,IAAS;QAC7B,IAAI,eAAe,GAAG,CAAC,CAAC;QACxB,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;gBACxB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBACvC,eAAe,IAAI,KAAK,CAAC,eAAe,CAAC;gBACzC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC;YAC/B,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5C,IAAI,YAAY,IAAI,IAAI;gBAAE,eAAe,EAAE,CAAC;YAC5C,IAAI,MAAM,IAAI,IAAI;gBAAE,SAAS,EAAE,CAAC;YAEhC,yBAAyB;YACzB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACzB,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;oBAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;oBAC7C,eAAe,IAAI,KAAK,CAAC,eAAe,CAAC;oBACzC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC;gBAC/B,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;IACxC,CAAC;IAEO,cAAc,CAAC,IAAS,EAAE,QAAgB;QAChD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC/D,CAAC;aAAM,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;YAE5B,4BAA4B;YAC5B,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;gBACrD,OAAO,OAAO,CAAC,UAAU,CAAC;YAC5B,CAAC;YACD,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBAC/C,OAAO,OAAO,CAAC,IAAI,CAAC;YACtB,CAAC;YAED,2BAA2B;YAC3B,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;gBAC5B,MAAM,cAAc,GAAQ,EAAE,CAAC;gBAC/B,KAAK,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;oBAC/E,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;gBACzE,CAAC;gBACD,OAAO,CAAC,eAAe,GAAG,cAAc,CAAC;YAC3C,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAhMD,4CAgMC","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}"]}
@@ -41,5 +41,7 @@ export declare class PushService {
41
41
  private processRelatedEntities;
42
42
  private isValidRecordData;
43
43
  private getRecordKey;
44
+ private formatFieldValue;
44
45
  private findEntityDirectories;
46
+ private findEntityDirectoriesRecursive;
45
47
  }
@@ -28,19 +28,62 @@ class PushService {
28
28
  this.processedRecords.clear();
29
29
  const fileBackupManager = new file_backup_manager_1.FileBackupManager();
30
30
  // Load sync config for SQL logging settings and autoCreateMissingRecords flag
31
- this.syncConfig = await (0, config_1.loadSyncConfig)(config_manager_1.configManager.getOriginalCwd());
31
+ // If dir option is specified, load from that directory, otherwise use original CWD
32
+ const configDir = options.dir ? path_1.default.resolve(config_manager_1.configManager.getOriginalCwd(), options.dir) : config_manager_1.configManager.getOriginalCwd();
33
+ this.syncConfig = await (0, config_1.loadSyncConfig)(configDir);
34
+ if (options.verbose) {
35
+ callbacks?.onLog?.(`Original working directory: ${config_manager_1.configManager.getOriginalCwd()}`);
36
+ callbacks?.onLog?.(`Config directory (with dir option): ${configDir}`);
37
+ callbacks?.onLog?.(`Config file path: ${path_1.default.join(configDir, '.mj-sync.json')}`);
38
+ callbacks?.onLog?.(`Full sync config loaded: ${JSON.stringify(this.syncConfig, null, 2)}`);
39
+ callbacks?.onLog?.(`SQL logging config: ${JSON.stringify(this.syncConfig?.sqlLogging)}`);
40
+ }
32
41
  const sqlLogger = new sql_logger_1.SQLLogger(this.syncConfig);
33
42
  const transactionManager = new transaction_manager_1.TransactionManager(sqlLogger);
43
+ if (options.verbose) {
44
+ callbacks?.onLog?.(`SQLLogger enabled status: ${sqlLogger.enabled}`);
45
+ }
46
+ // Setup SQL logging session with the provider if enabled
47
+ let sqlLoggingSession = null;
34
48
  try {
35
- // Initialize SQL logger if enabled
49
+ // Initialize SQL logger if enabled and not dry-run
36
50
  if (sqlLogger.enabled && !options.dryRun) {
37
- await sqlLogger.initialize();
51
+ const provider = core_1.Metadata.Provider;
38
52
  if (options.verbose) {
39
- callbacks?.onLog?.('📝 SQL logging enabled');
53
+ callbacks?.onLog?.(`SQL logging enabled: ${sqlLogger.enabled}`);
54
+ callbacks?.onLog?.(`Provider type: ${provider?.constructor?.name || 'Unknown'}`);
55
+ callbacks?.onLog?.(`Has CreateSqlLogger: ${typeof provider?.CreateSqlLogger === 'function'}`);
56
+ }
57
+ if (provider && typeof provider.CreateSqlLogger === 'function') {
58
+ // Generate filename with timestamp
59
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
60
+ const filename = this.syncConfig.sqlLogging?.formatAsMigration
61
+ ? `MetadataSync_Push_${timestamp}.sql`
62
+ : `push_${timestamp}.sql`;
63
+ // Use .sql-log-push directory in the config directory (where sync was initiated)
64
+ const outputDir = path_1.default.join(configDir, this.syncConfig?.sqlLogging?.outputDirectory || './sql-log-push');
65
+ const filepath = path_1.default.join(outputDir, filename);
66
+ // Ensure the directory exists
67
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(filepath));
68
+ // Create the SQL logging session
69
+ sqlLoggingSession = await provider.CreateSqlLogger(filepath, {
70
+ formatAsMigration: this.syncConfig.sqlLogging?.formatAsMigration || false,
71
+ description: 'MetadataSync push operation',
72
+ statementTypes: "mutations",
73
+ prettyPrint: true,
74
+ });
75
+ if (options.verbose) {
76
+ callbacks?.onLog?.(`📝 SQL logging enabled: ${filepath}`);
77
+ }
78
+ }
79
+ else {
80
+ if (options.verbose) {
81
+ callbacks?.onWarn?.('SQL logging requested but provider does not support it');
82
+ }
40
83
  }
41
84
  }
42
85
  // Find entity directories to process
43
- const entityDirs = this.findEntityDirectories(config_manager_1.configManager.getOriginalCwd(), options.dir);
86
+ const entityDirs = this.findEntityDirectories(configDir);
44
87
  if (entityDirs.length === 0) {
45
88
  throw new Error('No entity directories found');
46
89
  }
@@ -119,11 +162,12 @@ class PushService {
119
162
  callbacks?.onLog?.('✅ File backups committed');
120
163
  }
121
164
  }
122
- // Write SQL log if enabled
165
+ // Close SQL logging session if it was created
123
166
  let sqlLogPath;
124
- if (sqlLogger.enabled && !options.dryRun) {
125
- sqlLogPath = await sqlLogger.writeLog();
126
- if (sqlLogPath && options.verbose) {
167
+ if (sqlLoggingSession) {
168
+ sqlLogPath = sqlLoggingSession.filePath;
169
+ await sqlLoggingSession.dispose();
170
+ if (options.verbose) {
127
171
  callbacks?.onLog?.(`📝 SQL log written to: ${sqlLogPath}`);
128
172
  }
129
173
  }
@@ -147,6 +191,15 @@ class PushService {
147
191
  callbacks?.onWarn?.(`Failed to rollback file backups: ${rollbackError}`);
148
192
  }
149
193
  }
194
+ // Close SQL logging session on error
195
+ if (sqlLoggingSession) {
196
+ try {
197
+ await sqlLoggingSession.dispose();
198
+ }
199
+ catch (disposeError) {
200
+ callbacks?.onWarn?.(`Failed to close SQL logging session: ${disposeError}`);
201
+ }
202
+ }
150
203
  throw error;
151
204
  }
152
205
  }
@@ -161,6 +214,7 @@ class PushService {
161
214
  cwd: entityDir,
162
215
  absolute: true,
163
216
  onlyFiles: true,
217
+ dot: true,
164
218
  ignore: ['**/node_modules/**', '**/.mj-*.json']
165
219
  });
166
220
  if (options.verbose) {
@@ -169,37 +223,59 @@ class PushService {
169
223
  // Process each file
170
224
  for (const filePath of files) {
171
225
  try {
226
+ // Backup the file before any modifications (unless dry-run)
227
+ if (!options.dryRun) {
228
+ await fileBackupManager.backupFile(filePath);
229
+ }
172
230
  const fileData = await fs_extra_1.default.readJson(filePath);
173
231
  const records = Array.isArray(fileData) ? fileData : [fileData];
232
+ const isArray = Array.isArray(fileData);
174
233
  for (let i = 0; i < records.length; i++) {
175
234
  const recordData = records[i];
176
235
  if (!this.isValidRecordData(recordData)) {
177
- callbacks?.onWarn?.(`Invalid record format in ${filePath}${Array.isArray(fileData) ? ` at index ${i}` : ''}`);
236
+ callbacks?.onWarn?.(`Invalid record format in ${filePath}${isArray ? ` at index ${i}` : ''}`);
178
237
  errors++;
179
238
  continue;
180
239
  }
181
240
  try {
182
- const result = await this.processRecord(recordData, entityConfig, entityDir, options, callbacks);
241
+ // For arrays, work with a deep copy to avoid modifying the original
242
+ const recordToProcess = isArray ? JSON.parse(JSON.stringify(recordData)) : recordData;
243
+ const result = await this.processRecord(recordToProcess, entityConfig, entityDir, options, callbacks, filePath, isArray ? i : undefined);
183
244
  if (result === 'created')
184
245
  created++;
185
246
  else if (result === 'updated')
186
247
  updated++;
187
248
  else if (result === 'unchanged')
188
249
  unchanged++;
250
+ // For arrays, update the original record's primaryKey and sync only
251
+ if (isArray) {
252
+ // Update primaryKey if it exists (for new records)
253
+ if (recordToProcess.primaryKey) {
254
+ records[i].primaryKey = recordToProcess.primaryKey;
255
+ }
256
+ // Update sync metadata only if it was updated (dirty records only)
257
+ if (recordToProcess.sync) {
258
+ records[i].sync = recordToProcess.sync;
259
+ }
260
+ }
189
261
  // Track processed record
190
262
  const recordKey = this.getRecordKey(recordData, entityConfig.entity);
191
263
  this.processedRecords.set(recordKey, {
192
264
  filePath,
193
- arrayIndex: Array.isArray(fileData) ? i : undefined,
265
+ arrayIndex: isArray ? i : undefined,
194
266
  lineNumber: i + 1 // Simple line number approximation
195
267
  });
196
268
  }
197
269
  catch (recordError) {
198
- const errorMsg = `Error processing record in ${filePath}${Array.isArray(fileData) ? ` at index ${i}` : ''}: ${recordError}`;
270
+ const errorMsg = `Error processing record in ${filePath}${isArray ? ` at index ${i}` : ''}: ${recordError}`;
199
271
  callbacks?.onError?.(errorMsg);
200
272
  errors++;
201
273
  }
202
274
  }
275
+ // Write back the entire file if it's an array (after processing all records)
276
+ if (isArray && !options.dryRun) {
277
+ await fs_extra_1.default.writeJson(filePath, records, { spaces: 2 });
278
+ }
203
279
  }
204
280
  catch (fileError) {
205
281
  const errorMsg = `Error reading file ${filePath}: ${fileError}`;
@@ -209,12 +285,8 @@ class PushService {
209
285
  }
210
286
  return { created, updated, unchanged, errors };
211
287
  }
212
- async processRecord(recordData, entityConfig, entityDir, options, callbacks) {
288
+ async processRecord(recordData, entityConfig, entityDir, options, callbacks, filePath, arrayIndex) {
213
289
  const metadata = new core_1.Metadata();
214
- const entityInfo = metadata.EntityByName(entityConfig.entity);
215
- if (!entityInfo) {
216
- throw new Error(`Entity ${entityConfig.entity} not found in metadata`);
217
- }
218
290
  // Get or create entity instance
219
291
  let entity = await metadata.GetEntityObject(entityConfig.entity, this.contextUser);
220
292
  if (!entity) {
@@ -222,12 +294,13 @@ class PushService {
222
294
  }
223
295
  // Apply defaults from configuration
224
296
  const defaults = { ...entityConfig.defaults };
225
- // Build full record data
297
+ // Build full record data - keep original values for file writing
298
+ const originalFields = { ...recordData.fields };
226
299
  const fullData = {
227
300
  ...defaults,
228
301
  ...recordData.fields
229
302
  };
230
- // Process field values
303
+ // Process field values for database operations
231
304
  const processedData = {};
232
305
  for (const [fieldName, fieldValue] of Object.entries(fullData)) {
233
306
  const processedValue = await this.syncEngine.processFieldValue(fieldValue, entityDir, null, // parentRecord
@@ -238,29 +311,26 @@ class PushService {
238
311
  // Check if record exists
239
312
  const primaryKey = recordData.primaryKey;
240
313
  let exists = false;
241
- let existingEntity = null;
314
+ let isNew = false;
242
315
  if (primaryKey && Object.keys(primaryKey).length > 0) {
243
316
  // Try to load existing record
244
317
  const compositeKey = new core_1.CompositeKey();
245
318
  compositeKey.LoadFromSimpleObject(primaryKey);
246
- existingEntity = await metadata.GetEntityObject(entityConfig.entity, this.contextUser);
247
- if (existingEntity) {
248
- exists = await existingEntity.InnerLoad(compositeKey);
249
- // Check autoCreateMissingRecords flag if record not found
250
- if (!exists) {
251
- const autoCreate = this.syncConfig?.push?.autoCreateMissingRecords ?? false;
252
- const pkDisplay = Object.entries(primaryKey)
253
- .map(([key, value]) => `${key}=${value}`)
254
- .join(', ');
255
- if (!autoCreate) {
256
- const warning = `Record not found: ${entityConfig.entity} with primaryKey {${pkDisplay}}. To auto-create missing records, set push.autoCreateMissingRecords=true in .mj-sync.json`;
257
- this.warnings.push(warning);
258
- callbacks?.onWarn?.(warning);
259
- return 'error';
260
- }
261
- else if (options.verbose) {
262
- callbacks?.onLog?.(`Auto-creating missing ${entityConfig.entity} record with primaryKey {${pkDisplay}}`);
263
- }
319
+ exists = await entity.InnerLoad(compositeKey);
320
+ // Check autoCreateMissingRecords flag if record not found
321
+ if (!exists) {
322
+ const autoCreate = this.syncConfig?.push?.autoCreateMissingRecords ?? false;
323
+ const pkDisplay = Object.entries(primaryKey)
324
+ .map(([key, value]) => `${key}=${value}`)
325
+ .join(', ');
326
+ if (!autoCreate) {
327
+ const warning = `Record not found: ${entityConfig.entity} with primaryKey {${pkDisplay}}. To auto-create missing records, set push.autoCreateMissingRecords=true in .mj-sync.json`;
328
+ this.warnings.push(warning);
329
+ callbacks?.onWarn?.(warning);
330
+ return 'error';
331
+ }
332
+ else if (options.verbose) {
333
+ callbacks?.onLog?.(`Auto-creating missing ${entityConfig.entity} record with primaryKey {${pkDisplay}}`);
264
334
  }
265
335
  }
266
336
  }
@@ -274,11 +344,11 @@ class PushService {
274
344
  return 'created';
275
345
  }
276
346
  }
277
- // Use existing entity if found, otherwise create new one
278
347
  if (!exists) {
279
- entity = existingEntity || entity;
280
- entity.NewRecord();
281
- // Set primary key values for new records if provided
348
+ entity.NewRecord(); // make sure our record starts out fresh
349
+ isNew = true;
350
+ // UUID generation now happens automatically in BaseEntity.NewRecord()
351
+ // Set primary key values for new records if provided, this is important for the auto-create logic
282
352
  if (primaryKey) {
283
353
  for (const [pkField, pkValue] of Object.entries(primaryKey)) {
284
354
  entity.Set(pkField, pkValue);
@@ -294,18 +364,87 @@ class PushService {
294
364
  // Store related entities to process after parent save
295
365
  entity.__pendingRelatedEntities = recordData.relatedEntities;
296
366
  }
297
- // Save the record
298
- // TODO: Hook into BaseEntity SQL execution for SQL logging
299
- // Currently SQL logging requires deeper integration with MJ core
367
+ // Check if the record is actually dirty before considering it changed
368
+ let isDirty = entity.Dirty;
369
+ // Also check if file content has changed (for @file references)
370
+ if (!isDirty && !isNew && recordData.sync) {
371
+ const currentChecksum = await this.syncEngine.calculateChecksumWithFileContent(originalFields, entityDir);
372
+ if (currentChecksum !== recordData.sync.checksum) {
373
+ isDirty = true;
374
+ if (options.verbose) {
375
+ callbacks?.onLog?.(`📄 File content changed for ${entityConfig.entity} record (checksum mismatch)`);
376
+ }
377
+ }
378
+ }
379
+ // If updating an existing record that's dirty, show what changed
380
+ if (!isNew && isDirty) {
381
+ const changes = entity.GetChangesSinceLastSave();
382
+ const changeKeys = Object.keys(changes);
383
+ if (changeKeys.length > 0) {
384
+ // Get primary key info for display
385
+ const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);
386
+ const primaryKeyDisplay = [];
387
+ if (entityInfo) {
388
+ for (const pk of entityInfo.PrimaryKeys) {
389
+ primaryKeyDisplay.push(`${pk.Name}: ${entity.Get(pk.Name)}`);
390
+ }
391
+ }
392
+ callbacks?.onLog?.(`📝 Updating ${entityConfig.entity} record:`);
393
+ if (primaryKeyDisplay.length > 0) {
394
+ callbacks?.onLog?.(` Primary Key: ${primaryKeyDisplay.join(', ')}`);
395
+ }
396
+ callbacks?.onLog?.(` Changes:`);
397
+ for (const fieldName of changeKeys) {
398
+ const field = entity.GetFieldByName(fieldName);
399
+ const oldValue = field ? field.OldValue : undefined;
400
+ const newValue = changes[fieldName];
401
+ callbacks?.onLog?.(` ${fieldName}: ${this.formatFieldValue(oldValue)} → ${this.formatFieldValue(newValue)}`);
402
+ }
403
+ }
404
+ }
405
+ // Save the record (always call Save, but track if it was actually dirty)
300
406
  const saveResult = await entity.Save();
301
407
  if (!saveResult) {
302
408
  throw new Error(`Failed to save ${entityConfig.entity} record: ${entity.LatestResult?.Message || 'Unknown error'}`);
303
409
  }
410
+ // Update primaryKey for new records
411
+ if (isNew) {
412
+ const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);
413
+ if (entityInfo) {
414
+ const newPrimaryKey = {};
415
+ for (const pk of entityInfo.PrimaryKeys) {
416
+ newPrimaryKey[pk.Name] = entity.Get(pk.Name);
417
+ }
418
+ recordData.primaryKey = newPrimaryKey;
419
+ }
420
+ }
421
+ // Only update sync metadata if the record was actually dirty (changed)
422
+ if (isNew || isDirty) {
423
+ recordData.sync = {
424
+ lastModified: new Date().toISOString(),
425
+ checksum: await this.syncEngine.calculateChecksumWithFileContent(originalFields, entityDir)
426
+ };
427
+ }
428
+ // Restore original field values to preserve @ references
429
+ recordData.fields = originalFields;
430
+ // Write back to file only if it's a single record (not part of an array)
431
+ if (filePath && arrayIndex === undefined && !options.dryRun) {
432
+ await fs_extra_1.default.writeJson(filePath, recordData, { spaces: 2 });
433
+ }
304
434
  // Process related entities after parent save
305
435
  if (recordData.relatedEntities) {
306
436
  await this.processRelatedEntities(entity, recordData.relatedEntities, entityDir, options, callbacks);
307
437
  }
308
- return exists ? 'updated' : 'created';
438
+ // Return the actual status based on whether the record was dirty
439
+ if (isNew) {
440
+ return 'created';
441
+ }
442
+ else if (isDirty) {
443
+ return 'updated';
444
+ }
445
+ else {
446
+ return 'unchanged';
447
+ }
309
448
  }
310
449
  async processRelatedEntities(parentEntity, relatedEntities, entityDir, options, callbacks) {
311
450
  // TODO: Complete implementation for processing related entities
@@ -316,7 +455,7 @@ class PushService {
316
455
  // 4. Support nested related entities recursively
317
456
  for (const [key, records] of Object.entries(relatedEntities)) {
318
457
  for (const relatedRecord of records) {
319
- // Process @parent references
458
+ // Process @parent references but DON'T modify the original fields
320
459
  const processedFields = {};
321
460
  for (const [fieldName, fieldValue] of Object.entries(relatedRecord.fields)) {
322
461
  if (typeof fieldValue === 'string' && fieldValue.startsWith('@parent:')) {
@@ -327,8 +466,9 @@ class PushService {
327
466
  processedFields[fieldName] = await this.syncEngine.processFieldValue(fieldValue, entityDir, parentEntity, null);
328
467
  }
329
468
  }
330
- // Save related entity (simplified - full implementation needed)
331
- relatedRecord.fields = processedFields;
469
+ // TODO: Actually save the related entity with processedFields
470
+ // For now, we're just processing the values but not saving
471
+ // This needs to be implemented to actually create/update the related entities
332
472
  }
333
473
  }
334
474
  }
@@ -350,44 +490,69 @@ class PushService {
350
490
  const fieldKeys = Object.keys(recordData.fields).sort().join(',');
351
491
  return `${entityName}|fields:${fieldKeys}`;
352
492
  }
493
+ formatFieldValue(value, maxLength = 50) {
494
+ let strValue = JSON.stringify(value);
495
+ strValue = strValue.trim();
496
+ if (strValue.length > maxLength) {
497
+ return strValue.substring(0, maxLength) + '...';
498
+ }
499
+ return strValue;
500
+ }
353
501
  findEntityDirectories(baseDir, specificDir) {
354
502
  const dirs = [];
355
503
  if (specificDir) {
356
504
  // Process specific directory
357
505
  const fullPath = path_1.default.resolve(baseDir, specificDir);
358
506
  if (fs_extra_1.default.existsSync(fullPath) && fs_extra_1.default.statSync(fullPath).isDirectory()) {
359
- dirs.push(fullPath);
507
+ // Check if this directory has an entity configuration
508
+ const configPath = path_1.default.join(fullPath, '.mj-sync.json');
509
+ if (fs_extra_1.default.existsSync(configPath)) {
510
+ try {
511
+ const config = fs_extra_1.default.readJsonSync(configPath);
512
+ if (config.entity) {
513
+ // It's an entity directory, add it
514
+ dirs.push(fullPath);
515
+ }
516
+ else {
517
+ // It's a container directory, search its subdirectories
518
+ this.findEntityDirectoriesRecursive(fullPath, dirs);
519
+ }
520
+ }
521
+ catch {
522
+ // Invalid config, skip
523
+ }
524
+ }
360
525
  }
361
526
  }
362
527
  else {
363
528
  // Find all entity directories
364
- const searchDirs = (dir) => {
365
- const entries = fs_extra_1.default.readdirSync(dir, { withFileTypes: true });
366
- for (const entry of entries) {
367
- if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
368
- const fullPath = path_1.default.join(dir, entry.name);
369
- const configPath = path_1.default.join(fullPath, '.mj-sync.json');
370
- if (fs_extra_1.default.existsSync(configPath)) {
371
- try {
372
- const config = fs_extra_1.default.readJsonSync(configPath);
373
- if (config.entity) {
374
- dirs.push(fullPath);
375
- }
376
- }
377
- catch {
378
- // Skip invalid config files
379
- }
380
- }
381
- else {
382
- // Recurse into subdirectories
383
- searchDirs(fullPath);
529
+ this.findEntityDirectoriesRecursive(baseDir, dirs);
530
+ }
531
+ return dirs;
532
+ }
533
+ findEntityDirectoriesRecursive(dir, dirs) {
534
+ const entries = fs_extra_1.default.readdirSync(dir, { withFileTypes: true });
535
+ for (const entry of entries) {
536
+ if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
537
+ const fullPath = path_1.default.join(dir, entry.name);
538
+ const configPath = path_1.default.join(fullPath, '.mj-sync.json');
539
+ if (fs_extra_1.default.existsSync(configPath)) {
540
+ try {
541
+ const config = fs_extra_1.default.readJsonSync(configPath);
542
+ if (config.entity) {
543
+ dirs.push(fullPath);
384
544
  }
385
545
  }
546
+ catch {
547
+ // Skip invalid config files
548
+ }
386
549
  }
387
- };
388
- searchDirs(baseDir);
550
+ else {
551
+ // Recurse into subdirectories
552
+ this.findEntityDirectoriesRecursive(fullPath, dirs);
553
+ }
554
+ }
389
555
  }
390
- return dirs;
391
556
  }
392
557
  }
393
558
  exports.PushService = PushService;