@memberjunction/metadata-sync 2.56.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.
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/provider-utils.d.ts +2 -2
- package/dist/lib/provider-utils.js.map +1 -1
- package/dist/lib/sql-logger.js +3 -3
- package/dist/lib/sql-logger.js.map +1 -1
- package/dist/lib/sync-engine.d.ts +25 -0
- package/dist/lib/sync-engine.js +70 -0
- package/dist/lib/sync-engine.js.map +1 -1
- package/dist/lib/transaction-manager.d.ts +0 -1
- package/dist/lib/transaction-manager.js +4 -21
- package/dist/lib/transaction-manager.js.map +1 -1
- package/dist/services/FileResetService.js +1 -0
- package/dist/services/FileResetService.js.map +1 -1
- package/dist/services/PushService.d.ts +2 -0
- package/dist/services/PushService.js +238 -74
- package/dist/services/PushService.js.map +1 -1
- package/dist/services/WatchService.d.ts +2 -0
- package/dist/services/WatchService.js +63 -9
- package/dist/services/WatchService.js.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction-manager.js","sourceRoot":"","sources":["../../src/lib/transaction-manager.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAEH,qDAAmD;
|
|
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}"]}
|
|
@@ -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}"]}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
51
|
+
const provider = core_1.Metadata.Provider;
|
|
38
52
|
if (options.verbose) {
|
|
39
|
-
callbacks?.onLog?.(
|
|
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(
|
|
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
|
-
//
|
|
165
|
+
// Close SQL logging session if it was created
|
|
123
166
|
let sqlLogPath;
|
|
124
|
-
if (
|
|
125
|
-
sqlLogPath =
|
|
126
|
-
|
|
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}${
|
|
236
|
+
callbacks?.onWarn?.(`Invalid record format in ${filePath}${isArray ? ` at index ${i}` : ''}`);
|
|
178
237
|
errors++;
|
|
179
238
|
continue;
|
|
180
239
|
}
|
|
181
240
|
try {
|
|
182
|
-
|
|
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:
|
|
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}${
|
|
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
|
|
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
|
-
|
|
247
|
-
if
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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,10 @@ 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
|
|
280
|
-
|
|
281
|
-
// Set primary key values for new records if provided
|
|
348
|
+
entity.NewRecord(); // make sure our record starts out fresh
|
|
349
|
+
isNew = true;
|
|
350
|
+
// Set primary key values for new records if provided, this is important for the auto-create logic
|
|
282
351
|
if (primaryKey) {
|
|
283
352
|
for (const [pkField, pkValue] of Object.entries(primaryKey)) {
|
|
284
353
|
entity.Set(pkField, pkValue);
|
|
@@ -294,18 +363,87 @@ class PushService {
|
|
|
294
363
|
// Store related entities to process after parent save
|
|
295
364
|
entity.__pendingRelatedEntities = recordData.relatedEntities;
|
|
296
365
|
}
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
//
|
|
366
|
+
// Check if the record is actually dirty before considering it changed
|
|
367
|
+
let isDirty = entity.Dirty;
|
|
368
|
+
// Also check if file content has changed (for @file references)
|
|
369
|
+
if (!isDirty && !isNew && recordData.sync) {
|
|
370
|
+
const currentChecksum = await this.syncEngine.calculateChecksumWithFileContent(originalFields, entityDir);
|
|
371
|
+
if (currentChecksum !== recordData.sync.checksum) {
|
|
372
|
+
isDirty = true;
|
|
373
|
+
if (options.verbose) {
|
|
374
|
+
callbacks?.onLog?.(`📄 File content changed for ${entityConfig.entity} record (checksum mismatch)`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// If updating an existing record that's dirty, show what changed
|
|
379
|
+
if (!isNew && isDirty) {
|
|
380
|
+
const changes = entity.GetChangesSinceLastSave();
|
|
381
|
+
const changeKeys = Object.keys(changes);
|
|
382
|
+
if (changeKeys.length > 0) {
|
|
383
|
+
// Get primary key info for display
|
|
384
|
+
const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);
|
|
385
|
+
const primaryKeyDisplay = [];
|
|
386
|
+
if (entityInfo) {
|
|
387
|
+
for (const pk of entityInfo.PrimaryKeys) {
|
|
388
|
+
primaryKeyDisplay.push(`${pk.Name}: ${entity.Get(pk.Name)}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
callbacks?.onLog?.(`📝 Updating ${entityConfig.entity} record:`);
|
|
392
|
+
if (primaryKeyDisplay.length > 0) {
|
|
393
|
+
callbacks?.onLog?.(` Primary Key: ${primaryKeyDisplay.join(', ')}`);
|
|
394
|
+
}
|
|
395
|
+
callbacks?.onLog?.(` Changes:`);
|
|
396
|
+
for (const fieldName of changeKeys) {
|
|
397
|
+
const field = entity.GetFieldByName(fieldName);
|
|
398
|
+
const oldValue = field ? field.OldValue : undefined;
|
|
399
|
+
const newValue = changes[fieldName];
|
|
400
|
+
callbacks?.onLog?.(` ${fieldName}: ${this.formatFieldValue(oldValue)} → ${this.formatFieldValue(newValue)}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Save the record (always call Save, but track if it was actually dirty)
|
|
300
405
|
const saveResult = await entity.Save();
|
|
301
406
|
if (!saveResult) {
|
|
302
407
|
throw new Error(`Failed to save ${entityConfig.entity} record: ${entity.LatestResult?.Message || 'Unknown error'}`);
|
|
303
408
|
}
|
|
409
|
+
// Update primaryKey for new records
|
|
410
|
+
if (isNew) {
|
|
411
|
+
const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);
|
|
412
|
+
if (entityInfo) {
|
|
413
|
+
const newPrimaryKey = {};
|
|
414
|
+
for (const pk of entityInfo.PrimaryKeys) {
|
|
415
|
+
newPrimaryKey[pk.Name] = entity.Get(pk.Name);
|
|
416
|
+
}
|
|
417
|
+
recordData.primaryKey = newPrimaryKey;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// Only update sync metadata if the record was actually dirty (changed)
|
|
421
|
+
if (isNew || isDirty) {
|
|
422
|
+
recordData.sync = {
|
|
423
|
+
lastModified: new Date().toISOString(),
|
|
424
|
+
checksum: await this.syncEngine.calculateChecksumWithFileContent(originalFields, entityDir)
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
// Restore original field values to preserve @ references
|
|
428
|
+
recordData.fields = originalFields;
|
|
429
|
+
// Write back to file only if it's a single record (not part of an array)
|
|
430
|
+
if (filePath && arrayIndex === undefined && !options.dryRun) {
|
|
431
|
+
await fs_extra_1.default.writeJson(filePath, recordData, { spaces: 2 });
|
|
432
|
+
}
|
|
304
433
|
// Process related entities after parent save
|
|
305
434
|
if (recordData.relatedEntities) {
|
|
306
435
|
await this.processRelatedEntities(entity, recordData.relatedEntities, entityDir, options, callbacks);
|
|
307
436
|
}
|
|
308
|
-
|
|
437
|
+
// Return the actual status based on whether the record was dirty
|
|
438
|
+
if (isNew) {
|
|
439
|
+
return 'created';
|
|
440
|
+
}
|
|
441
|
+
else if (isDirty) {
|
|
442
|
+
return 'updated';
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
return 'unchanged';
|
|
446
|
+
}
|
|
309
447
|
}
|
|
310
448
|
async processRelatedEntities(parentEntity, relatedEntities, entityDir, options, callbacks) {
|
|
311
449
|
// TODO: Complete implementation for processing related entities
|
|
@@ -316,7 +454,7 @@ class PushService {
|
|
|
316
454
|
// 4. Support nested related entities recursively
|
|
317
455
|
for (const [key, records] of Object.entries(relatedEntities)) {
|
|
318
456
|
for (const relatedRecord of records) {
|
|
319
|
-
// Process @parent references
|
|
457
|
+
// Process @parent references but DON'T modify the original fields
|
|
320
458
|
const processedFields = {};
|
|
321
459
|
for (const [fieldName, fieldValue] of Object.entries(relatedRecord.fields)) {
|
|
322
460
|
if (typeof fieldValue === 'string' && fieldValue.startsWith('@parent:')) {
|
|
@@ -327,8 +465,9 @@ class PushService {
|
|
|
327
465
|
processedFields[fieldName] = await this.syncEngine.processFieldValue(fieldValue, entityDir, parentEntity, null);
|
|
328
466
|
}
|
|
329
467
|
}
|
|
330
|
-
//
|
|
331
|
-
|
|
468
|
+
// TODO: Actually save the related entity with processedFields
|
|
469
|
+
// For now, we're just processing the values but not saving
|
|
470
|
+
// This needs to be implemented to actually create/update the related entities
|
|
332
471
|
}
|
|
333
472
|
}
|
|
334
473
|
}
|
|
@@ -350,44 +489,69 @@ class PushService {
|
|
|
350
489
|
const fieldKeys = Object.keys(recordData.fields).sort().join(',');
|
|
351
490
|
return `${entityName}|fields:${fieldKeys}`;
|
|
352
491
|
}
|
|
492
|
+
formatFieldValue(value, maxLength = 50) {
|
|
493
|
+
let strValue = JSON.stringify(value);
|
|
494
|
+
strValue = strValue.trim();
|
|
495
|
+
if (strValue.length > maxLength) {
|
|
496
|
+
return strValue.substring(0, maxLength) + '...';
|
|
497
|
+
}
|
|
498
|
+
return strValue;
|
|
499
|
+
}
|
|
353
500
|
findEntityDirectories(baseDir, specificDir) {
|
|
354
501
|
const dirs = [];
|
|
355
502
|
if (specificDir) {
|
|
356
503
|
// Process specific directory
|
|
357
504
|
const fullPath = path_1.default.resolve(baseDir, specificDir);
|
|
358
505
|
if (fs_extra_1.default.existsSync(fullPath) && fs_extra_1.default.statSync(fullPath).isDirectory()) {
|
|
359
|
-
|
|
506
|
+
// Check if this directory has an entity configuration
|
|
507
|
+
const configPath = path_1.default.join(fullPath, '.mj-sync.json');
|
|
508
|
+
if (fs_extra_1.default.existsSync(configPath)) {
|
|
509
|
+
try {
|
|
510
|
+
const config = fs_extra_1.default.readJsonSync(configPath);
|
|
511
|
+
if (config.entity) {
|
|
512
|
+
// It's an entity directory, add it
|
|
513
|
+
dirs.push(fullPath);
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
// It's a container directory, search its subdirectories
|
|
517
|
+
this.findEntityDirectoriesRecursive(fullPath, dirs);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
// Invalid config, skip
|
|
522
|
+
}
|
|
523
|
+
}
|
|
360
524
|
}
|
|
361
525
|
}
|
|
362
526
|
else {
|
|
363
527
|
// Find all entity directories
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
// Recurse into subdirectories
|
|
383
|
-
searchDirs(fullPath);
|
|
528
|
+
this.findEntityDirectoriesRecursive(baseDir, dirs);
|
|
529
|
+
}
|
|
530
|
+
return dirs;
|
|
531
|
+
}
|
|
532
|
+
findEntityDirectoriesRecursive(dir, dirs) {
|
|
533
|
+
const entries = fs_extra_1.default.readdirSync(dir, { withFileTypes: true });
|
|
534
|
+
for (const entry of entries) {
|
|
535
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
536
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
537
|
+
const configPath = path_1.default.join(fullPath, '.mj-sync.json');
|
|
538
|
+
if (fs_extra_1.default.existsSync(configPath)) {
|
|
539
|
+
try {
|
|
540
|
+
const config = fs_extra_1.default.readJsonSync(configPath);
|
|
541
|
+
if (config.entity) {
|
|
542
|
+
dirs.push(fullPath);
|
|
384
543
|
}
|
|
385
544
|
}
|
|
545
|
+
catch {
|
|
546
|
+
// Skip invalid config files
|
|
547
|
+
}
|
|
386
548
|
}
|
|
387
|
-
|
|
388
|
-
|
|
549
|
+
else {
|
|
550
|
+
// Recurse into subdirectories
|
|
551
|
+
this.findEntityDirectoriesRecursive(fullPath, dirs);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
389
554
|
}
|
|
390
|
-
return dirs;
|
|
391
555
|
}
|
|
392
556
|
}
|
|
393
557
|
exports.PushService = PushService;
|