@memberjunction/metadata-sync 2.63.1 ā 2.65.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/README.md +48 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js.map +1 -1
- package/dist/services/PushService.js +2 -0
- package/dist/services/PushService.js.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1116,6 +1116,8 @@ SQL logging is configured in the root-level `.mj-sync.json` file only (not inher
|
|
|
1116
1116
|
| `enabled` | boolean | false | Whether to enable SQL logging during push operations |
|
|
1117
1117
|
| `outputDirectory` | string | "./sql_logging" | Directory to output SQL log files (relative to command execution directory) |
|
|
1118
1118
|
| `formatAsMigration` | boolean | false | Whether to format SQL as migration-ready files with Flyway schema placeholders |
|
|
1119
|
+
| `filterPatterns` | string[] | undefined | Array of patterns to filter SQL statements (see below) |
|
|
1120
|
+
| `filterType` | "exclude" \| "include" | "exclude" | How to apply filter patterns |
|
|
1119
1121
|
|
|
1120
1122
|
#### SQL Log File Format
|
|
1121
1123
|
|
|
@@ -1161,6 +1163,52 @@ Migration files include:
|
|
|
1161
1163
|
|
|
1162
1164
|
The SQL logging runs in parallel with the actual database operations, ensuring minimal performance impact while capturing all SQL statements for review and potential migration use.
|
|
1163
1165
|
|
|
1166
|
+
#### SQL Filtering Patterns
|
|
1167
|
+
|
|
1168
|
+
The `filterPatterns` option allows you to include or exclude specific SQL statements from logging. It supports both regex patterns and simple wildcard patterns:
|
|
1169
|
+
|
|
1170
|
+
**Pattern Types:**
|
|
1171
|
+
- **Regex patterns**: Start with `/` and optionally end with flags (e.g., `/spCreate.*Run/i`)
|
|
1172
|
+
- **Simple wildcards**: Use `*` as a wildcard (e.g., `*AIPrompt*`)
|
|
1173
|
+
|
|
1174
|
+
**Examples:**
|
|
1175
|
+
|
|
1176
|
+
```json
|
|
1177
|
+
{
|
|
1178
|
+
"sqlLogging": {
|
|
1179
|
+
"enabled": true,
|
|
1180
|
+
"filterPatterns": [
|
|
1181
|
+
"*AIPrompt*", // Exclude anything containing "AIPrompt"
|
|
1182
|
+
"/^EXEC sp_/i", // Exclude stored procedures starting with "sp_"
|
|
1183
|
+
"*EntityFieldValue*", // Exclude EntityFieldValue operations
|
|
1184
|
+
"/INSERT INTO (__mj|mj)/i" // Exclude inserts to system tables
|
|
1185
|
+
],
|
|
1186
|
+
"filterType": "exclude" // Default - exclude matching patterns
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
**Include Mode Example:**
|
|
1192
|
+
```json
|
|
1193
|
+
{
|
|
1194
|
+
"sqlLogging": {
|
|
1195
|
+
"enabled": true,
|
|
1196
|
+
"filterPatterns": [
|
|
1197
|
+
"*User*", // Only log User-related SQL
|
|
1198
|
+
"*Role*", // Only log Role-related SQL
|
|
1199
|
+
"/sp_ChangePassword/i" // Include password change procedures
|
|
1200
|
+
],
|
|
1201
|
+
"filterType": "include" // Only log statements matching patterns
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
**Simple Wildcard Syntax:**
|
|
1207
|
+
- `*pattern*` - Contains pattern (case-insensitive)
|
|
1208
|
+
- `pattern*` - Starts with pattern
|
|
1209
|
+
- `*pattern` - Ends with pattern
|
|
1210
|
+
- `pattern` - Exact match
|
|
1211
|
+
|
|
1164
1212
|
### User Role Validation (NEW)
|
|
1165
1213
|
|
|
1166
1214
|
MetadataSync now supports validating UserID fields against specific roles in the MemberJunction system. This ensures that only users with appropriate roles can be referenced in metadata files.
|
package/dist/config.d.ts
CHANGED
|
@@ -81,6 +81,20 @@ export interface SyncConfig {
|
|
|
81
81
|
outputDirectory?: string;
|
|
82
82
|
/** Whether to format SQL as migration-ready files with Flyway schema placeholders */
|
|
83
83
|
formatAsMigration?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Array of patterns to filter SQL statements.
|
|
86
|
+
* Supports both regex strings and simple wildcard patterns:
|
|
87
|
+
* - Regex: "/spCreate.*Run/i" (must start with "/" and optionally end with flags)
|
|
88
|
+
* - Simple: "*spCreateAIPromptRun*" (uses * as wildcard, case-insensitive by default)
|
|
89
|
+
* Examples: ["*AIPrompt*", "/^EXEC sp_/i", "*EntityFieldValue*"]
|
|
90
|
+
*/
|
|
91
|
+
filterPatterns?: string[];
|
|
92
|
+
/**
|
|
93
|
+
* Determines how filterPatterns are applied:
|
|
94
|
+
* - 'exclude': If ANY pattern matches, the SQL is NOT logged (default)
|
|
95
|
+
* - 'include': If ANY pattern matches, the SQL IS logged
|
|
96
|
+
*/
|
|
97
|
+
filterType?: 'exclude' | 'include';
|
|
84
98
|
};
|
|
85
99
|
/** Watch command configuration */
|
|
86
100
|
watch?: {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;AAGH,gDAAwB;AACxB,wDAA0B;AAC1B,yDAAqD;AAyOrD;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,YAAY;IAC1B,OAAO,8BAAa,CAAC,YAAY,EAAE,CAAC;AACtC,CAAC;AAFD,oCAEC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEnD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAbD,wCAaC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEnD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC7C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAfD,4CAeC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAErD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAbD,4CAaC","sourcesContent":["/**\n * @fileoverview Configuration types and loaders for MetadataSync\n * @module config\n * \n * This module defines configuration interfaces and provides utilities for loading\n * various configuration files used by the MetadataSync tool. It supports:\n * - MemberJunction database configuration (mj.config.cjs)\n * - Sync configuration (.mj-sync.json)\n * - Entity-specific configuration (.mj-sync.json with entity field)\n * - Folder-level defaults (.mj-folder.json)\n */\n\nimport { cosmiconfigSync } from 'cosmiconfig';\nimport path from 'path';\nimport fs from 'fs-extra';\nimport { configManager } from './lib/config-manager';\n\n/**\n * MemberJunction database configuration\n * \n * Defines connection parameters and settings for connecting to the MemberJunction\n * database. Typically loaded from mj.config.cjs in the project root.\n */\nexport interface MJConfig {\n /** Database server hostname or IP address */\n dbHost: string;\n /** Database server port (defaults to 1433 for SQL Server) */\n dbPort?: number;\n /** Database name to connect to */\n dbDatabase: string;\n /** Database authentication username */\n dbUsername: string;\n /** Database authentication password */\n dbPassword: string;\n /** Whether to trust the server certificate (Y/N) */\n dbTrustServerCertificate?: string;\n /** Whether to encrypt the connection (Y/N, auto-detected for Azure SQL) */\n dbEncrypt?: string;\n /** SQL Server instance name (for named instances) */\n dbInstanceName?: string;\n /** Schema name for MemberJunction core tables (defaults to __mj) */\n mjCoreSchema?: string;\n /** Allow additional properties for extensibility */\n [key: string]: any;\n}\n\n/**\n * Global sync configuration\n * \n * Defines settings that apply to the entire sync process, including push/pull\n * behaviors and watch mode configuration. Stored in .mj-sync.json at the root.\n */\nexport interface SyncConfig {\n /** Version of the sync configuration format */\n version: string;\n /** Glob pattern for finding data files (defaults to \"*.json\") */\n filePattern?: string;\n /** \n * Directory processing order (only applies to root-level config, not inherited by subdirectories)\n * Specifies the order in which subdirectories should be processed to handle dependencies.\n * Directories not listed in this array will be processed after the ordered ones in alphabetical order.\n */\n directoryOrder?: string[];\n /** \n * Directories to ignore during processing\n * Can be directory names or glob patterns relative to the location of the .mj-sync.json file\n * Cumulative: subdirectories inherit and add to parent ignoreDirectories\n * Examples: [\"output\", \"examples\", \"temp\"]\n */\n ignoreDirectories?: string[];\n /** Push command configuration */\n push?: {\n /** Whether to validate records before pushing to database */\n validateBeforePush?: boolean;\n /** Whether to require user confirmation before push */\n requireConfirmation?: boolean;\n /** \n * Whether to automatically create new records when a primaryKey exists but record is not found\n * Defaults to false - will warn instead of creating\n */\n autoCreateMissingRecords?: boolean;\n };\n /** SQL logging configuration (only applies to root-level config, not inherited by subdirectories) */\n sqlLogging?: {\n /** Whether to enable SQL logging during push operations */\n enabled?: boolean;\n /** Directory to output SQL log files (relative to command execution directory, defaults to './sql_logging') */\n outputDirectory?: string;\n /** Whether to format SQL as migration-ready files with Flyway schema placeholders */\n formatAsMigration?: boolean;\n };\n /** Watch command configuration */\n watch?: {\n /** Milliseconds to wait before processing file changes */\n debounceMs?: number;\n /** File patterns to ignore during watch */\n ignorePatterns?: string[];\n };\n /** User role validation configuration */\n userRoleValidation?: {\n /** Whether to enable user role validation for UserID fields */\n enabled?: boolean;\n /** List of role names that are allowed to be referenced in metadata */\n allowedRoles?: string[];\n /** Whether to allow users without any roles (defaults to false) */\n allowUsersWithoutRoles?: boolean;\n };\n}\n\n/**\n * Configuration for related entity synchronization\n * \n * Defines how to pull and push related entities that have foreign key relationships\n * with a parent entity. Supports nested relationships for deep object graphs.\n * NEW: Supports automatic recursive patterns for self-referencing entities.\n */\nexport interface RelatedEntityConfig {\n /** Name of the related entity to sync */\n entity: string;\n /** Field name that contains the foreign key reference to parent (e.g., \"PromptID\") */\n foreignKey: string;\n /** Optional SQL filter to apply when pulling related records */\n filter?: string;\n /** \n * Enable recursive fetching for self-referencing entities\n * When true, automatically fetches all levels of the hierarchy until no more children found\n */\n recursive?: boolean;\n /** \n * Maximum depth for recursive fetching (optional, defaults to 10)\n * Prevents infinite loops and controls memory usage\n * Only applies when recursive is true\n */\n maxDepth?: number;\n /** Fields to externalize to separate files for this related entity */\n externalizeFields?: string[] | {\n [fieldName: string]: {\n /** File extension to use (e.g., \".md\", \".txt\", \".html\") */\n extension?: string;\n }\n } | Array<{\n /** Field name to externalize */\n field: string;\n /** Pattern for the output file. Supports placeholders from the entity */\n pattern: string;\n }>;\n /** Fields to exclude from the pulled data for this related entity */\n excludeFields?: string[];\n /** Foreign key fields to convert to @lookup references for this related entity */\n lookupFields?: {\n [fieldName: string]: {\n entity: string;\n field: string;\n };\n };\n /** Nested related entities for deep object graphs */\n relatedEntities?: Record<string, RelatedEntityConfig>;\n}\n\n/**\n * Entity-specific configuration\n * \n * Defines settings for a specific entity type within a directory. Stored in\n * .mj-sync.json files that contain an \"entity\" field. Supports defaults,\n * file patterns, and related entity configuration.\n */\nexport interface EntityConfig {\n /** Name of the entity this directory contains */\n entity: string;\n /** Glob pattern for finding data files (defaults to \"*.json\") */\n filePattern?: string;\n /** Default field values applied to all records in this directory */\n defaults?: Record<string, any>;\n /** \n * Directories to ignore during processing\n * Can be directory names or glob patterns relative to the location of the .mj-sync.json file\n * Cumulative: subdirectories inherit and add to parent ignoreDirectories\n * Examples: [\"output\", \"examples\", \"temp\"]\n */\n ignoreDirectories?: string[];\n /** Pull command specific configuration */\n pull?: {\n /** Glob pattern for finding existing files to update (defaults to filePattern) */\n filePattern?: string;\n /** Whether to create new files for records not found locally */\n createNewFileIfNotFound?: boolean;\n /** Filename for new records when createNewFileIfNotFound is true */\n newFileName?: string;\n /** Whether to append multiple new records to a single file */\n appendRecordsToExistingFile?: boolean;\n /** Whether to update existing records found in local files */\n updateExistingRecords?: boolean;\n /** Fields to preserve during updates (never overwrite these) */\n preserveFields?: string[];\n /** Strategy for merging updates: \"overwrite\" | \"merge\" | \"skip\" */\n mergeStrategy?: 'overwrite' | 'merge' | 'skip';\n /** Create backup files before updating existing files */\n backupBeforeUpdate?: boolean;\n /** Directory name for backup files (defaults to \".backups\") */\n backupDirectory?: string;\n /** SQL filter to apply when pulling records from database */\n filter?: string;\n /** Configuration for pulling related entities */\n relatedEntities?: Record<string, RelatedEntityConfig>;\n /** Fields to externalize to separate files with optional configuration */\n externalizeFields?: string[] | {\n [fieldName: string]: {\n /** File extension to use (e.g., \".md\", \".txt\", \".html\") */\n extension?: string;\n }\n } | Array<{\n /** Field name to externalize */\n field: string;\n /** Pattern for the output file. Supports placeholders:\n * - {Name}: Entity's name field value\n * - {ID}: Entity's ID\n * - {FieldName}: The field being externalized\n * - Any other {FieldName} from the entity\n * Example: \"@file:templates/{Name}.template.md\"\n */\n pattern: string;\n }>;\n /** Fields to exclude from the pulled data (e.g., [\"TemplateID\"]) */\n excludeFields?: string[];\n /** Foreign key fields to convert to @lookup references */\n lookupFields?: {\n /** Field name in this entity (e.g., \"CategoryID\") */\n [fieldName: string]: {\n /** Target entity name (e.g., \"AI Prompt Categories\") */\n entity: string;\n /** Field in target entity to use for lookup (e.g., \"Name\") */\n field: string;\n };\n };\n };\n}\n\n/**\n * Folder-level configuration\n * \n * Defines default values that cascade down to all subdirectories. Stored in\n * .mj-folder.json files. Child folders can override parent defaults.\n */\nexport interface FolderConfig {\n /** Default field values that apply to all entities in this folder and subfolders */\n defaults: Record<string, any>;\n}\n\n/**\n * Load MemberJunction configuration from the filesystem\n * \n * Searches for mj.config.cjs starting from the current directory and walking up\n * the directory tree. Uses cosmiconfig for flexible configuration loading.\n * \n * @returns MJConfig object if found, null if not found or invalid\n * \n * @example\n * ```typescript\n * const config = loadMJConfig();\n * if (config) {\n * console.log(`Connecting to ${config.dbHost}:${config.dbPort || 1433}`);\n * }\n * ```\n */\nexport function loadMJConfig(): MJConfig | null {\n return configManager.loadMJConfig();\n}\n\n/**\n * Load sync configuration from a directory\n * \n * Loads .mj-sync.json file from the specified directory. This file can contain\n * either global sync configuration (no entity field) or entity-specific\n * configuration (with entity field).\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to SyncConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const syncConfig = await loadSyncConfig('/path/to/project');\n * if (syncConfig?.push?.requireConfirmation) {\n * // Show confirmation prompt\n * }\n * ```\n */\nexport async function loadSyncConfig(dir: string): Promise<SyncConfig | null> {\n const configPath = path.join(dir, '.mj-sync.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error('Error loading sync config:', error);\n return null;\n }\n }\n \n return null;\n}\n\n/**\n * Load entity-specific configuration from a directory\n * \n * Loads .mj-sync.json file that contains an \"entity\" field, indicating this\n * directory contains data for a specific entity type. Returns null if the\n * file doesn't exist or doesn't contain an entity field.\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to EntityConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const entityConfig = await loadEntityConfig('./ai-prompts');\n * if (entityConfig) {\n * console.log(`Directory contains ${entityConfig.entity} records`);\n * }\n * ```\n */\nexport async function loadEntityConfig(dir: string): Promise<EntityConfig | null> {\n const configPath = path.join(dir, '.mj-sync.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n const config = await fs.readJson(configPath);\n if (config.entity) {\n return config;\n }\n } catch (error) {\n console.error('Error loading entity config:', error);\n }\n }\n \n return null;\n}\n\n/**\n * Load folder-level configuration\n * \n * Loads .mj-folder.json file that contains default values to be applied to\n * all entities in this folder and its subfolders. Used for cascading defaults\n * in deep directory structures.\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to FolderConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const folderConfig = await loadFolderConfig('./templates');\n * if (folderConfig?.defaults) {\n * // Apply folder defaults to records\n * }\n * ```\n */\nexport async function loadFolderConfig(dir: string): Promise<FolderConfig | null> {\n const configPath = path.join(dir, '.mj-folder.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error('Error loading folder config:', error);\n return null;\n }\n }\n \n return null;\n}"]}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;AAGH,gDAAwB;AACxB,wDAA0B;AAC1B,yDAAqD;AAuPrD;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,YAAY;IAC1B,OAAO,8BAAa,CAAC,YAAY,EAAE,CAAC;AACtC,CAAC;AAFD,oCAEC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEnD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAbD,wCAaC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEnD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC7C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAfD,4CAeC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAErD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAbD,4CAaC","sourcesContent":["/**\n * @fileoverview Configuration types and loaders for MetadataSync\n * @module config\n * \n * This module defines configuration interfaces and provides utilities for loading\n * various configuration files used by the MetadataSync tool. It supports:\n * - MemberJunction database configuration (mj.config.cjs)\n * - Sync configuration (.mj-sync.json)\n * - Entity-specific configuration (.mj-sync.json with entity field)\n * - Folder-level defaults (.mj-folder.json)\n */\n\nimport { cosmiconfigSync } from 'cosmiconfig';\nimport path from 'path';\nimport fs from 'fs-extra';\nimport { configManager } from './lib/config-manager';\n\n/**\n * MemberJunction database configuration\n * \n * Defines connection parameters and settings for connecting to the MemberJunction\n * database. Typically loaded from mj.config.cjs in the project root.\n */\nexport interface MJConfig {\n /** Database server hostname or IP address */\n dbHost: string;\n /** Database server port (defaults to 1433 for SQL Server) */\n dbPort?: number;\n /** Database name to connect to */\n dbDatabase: string;\n /** Database authentication username */\n dbUsername: string;\n /** Database authentication password */\n dbPassword: string;\n /** Whether to trust the server certificate (Y/N) */\n dbTrustServerCertificate?: string;\n /** Whether to encrypt the connection (Y/N, auto-detected for Azure SQL) */\n dbEncrypt?: string;\n /** SQL Server instance name (for named instances) */\n dbInstanceName?: string;\n /** Schema name for MemberJunction core tables (defaults to __mj) */\n mjCoreSchema?: string;\n /** Allow additional properties for extensibility */\n [key: string]: any;\n}\n\n/**\n * Global sync configuration\n * \n * Defines settings that apply to the entire sync process, including push/pull\n * behaviors and watch mode configuration. Stored in .mj-sync.json at the root.\n */\nexport interface SyncConfig {\n /** Version of the sync configuration format */\n version: string;\n /** Glob pattern for finding data files (defaults to \"*.json\") */\n filePattern?: string;\n /** \n * Directory processing order (only applies to root-level config, not inherited by subdirectories)\n * Specifies the order in which subdirectories should be processed to handle dependencies.\n * Directories not listed in this array will be processed after the ordered ones in alphabetical order.\n */\n directoryOrder?: string[];\n /** \n * Directories to ignore during processing\n * Can be directory names or glob patterns relative to the location of the .mj-sync.json file\n * Cumulative: subdirectories inherit and add to parent ignoreDirectories\n * Examples: [\"output\", \"examples\", \"temp\"]\n */\n ignoreDirectories?: string[];\n /** Push command configuration */\n push?: {\n /** Whether to validate records before pushing to database */\n validateBeforePush?: boolean;\n /** Whether to require user confirmation before push */\n requireConfirmation?: boolean;\n /** \n * Whether to automatically create new records when a primaryKey exists but record is not found\n * Defaults to false - will warn instead of creating\n */\n autoCreateMissingRecords?: boolean;\n };\n /** SQL logging configuration (only applies to root-level config, not inherited by subdirectories) */\n sqlLogging?: {\n /** Whether to enable SQL logging during push operations */\n enabled?: boolean;\n /** Directory to output SQL log files (relative to command execution directory, defaults to './sql_logging') */\n outputDirectory?: string;\n /** Whether to format SQL as migration-ready files with Flyway schema placeholders */\n formatAsMigration?: boolean;\n /**\n * Array of patterns to filter SQL statements.\n * Supports both regex strings and simple wildcard patterns:\n * - Regex: \"/spCreate.*Run/i\" (must start with \"/\" and optionally end with flags)\n * - Simple: \"*spCreateAIPromptRun*\" (uses * as wildcard, case-insensitive by default)\n * Examples: [\"*AIPrompt*\", \"/^EXEC sp_/i\", \"*EntityFieldValue*\"]\n */\n filterPatterns?: string[];\n /**\n * Determines how filterPatterns are applied:\n * - 'exclude': If ANY pattern matches, the SQL is NOT logged (default)\n * - 'include': If ANY pattern matches, the SQL IS logged\n */\n filterType?: 'exclude' | 'include';\n };\n /** Watch command configuration */\n watch?: {\n /** Milliseconds to wait before processing file changes */\n debounceMs?: number;\n /** File patterns to ignore during watch */\n ignorePatterns?: string[];\n };\n /** User role validation configuration */\n userRoleValidation?: {\n /** Whether to enable user role validation for UserID fields */\n enabled?: boolean;\n /** List of role names that are allowed to be referenced in metadata */\n allowedRoles?: string[];\n /** Whether to allow users without any roles (defaults to false) */\n allowUsersWithoutRoles?: boolean;\n };\n}\n\n/**\n * Configuration for related entity synchronization\n * \n * Defines how to pull and push related entities that have foreign key relationships\n * with a parent entity. Supports nested relationships for deep object graphs.\n * NEW: Supports automatic recursive patterns for self-referencing entities.\n */\nexport interface RelatedEntityConfig {\n /** Name of the related entity to sync */\n entity: string;\n /** Field name that contains the foreign key reference to parent (e.g., \"PromptID\") */\n foreignKey: string;\n /** Optional SQL filter to apply when pulling related records */\n filter?: string;\n /** \n * Enable recursive fetching for self-referencing entities\n * When true, automatically fetches all levels of the hierarchy until no more children found\n */\n recursive?: boolean;\n /** \n * Maximum depth for recursive fetching (optional, defaults to 10)\n * Prevents infinite loops and controls memory usage\n * Only applies when recursive is true\n */\n maxDepth?: number;\n /** Fields to externalize to separate files for this related entity */\n externalizeFields?: string[] | {\n [fieldName: string]: {\n /** File extension to use (e.g., \".md\", \".txt\", \".html\") */\n extension?: string;\n }\n } | Array<{\n /** Field name to externalize */\n field: string;\n /** Pattern for the output file. Supports placeholders from the entity */\n pattern: string;\n }>;\n /** Fields to exclude from the pulled data for this related entity */\n excludeFields?: string[];\n /** Foreign key fields to convert to @lookup references for this related entity */\n lookupFields?: {\n [fieldName: string]: {\n entity: string;\n field: string;\n };\n };\n /** Nested related entities for deep object graphs */\n relatedEntities?: Record<string, RelatedEntityConfig>;\n}\n\n/**\n * Entity-specific configuration\n * \n * Defines settings for a specific entity type within a directory. Stored in\n * .mj-sync.json files that contain an \"entity\" field. Supports defaults,\n * file patterns, and related entity configuration.\n */\nexport interface EntityConfig {\n /** Name of the entity this directory contains */\n entity: string;\n /** Glob pattern for finding data files (defaults to \"*.json\") */\n filePattern?: string;\n /** Default field values applied to all records in this directory */\n defaults?: Record<string, any>;\n /** \n * Directories to ignore during processing\n * Can be directory names or glob patterns relative to the location of the .mj-sync.json file\n * Cumulative: subdirectories inherit and add to parent ignoreDirectories\n * Examples: [\"output\", \"examples\", \"temp\"]\n */\n ignoreDirectories?: string[];\n /** Pull command specific configuration */\n pull?: {\n /** Glob pattern for finding existing files to update (defaults to filePattern) */\n filePattern?: string;\n /** Whether to create new files for records not found locally */\n createNewFileIfNotFound?: boolean;\n /** Filename for new records when createNewFileIfNotFound is true */\n newFileName?: string;\n /** Whether to append multiple new records to a single file */\n appendRecordsToExistingFile?: boolean;\n /** Whether to update existing records found in local files */\n updateExistingRecords?: boolean;\n /** Fields to preserve during updates (never overwrite these) */\n preserveFields?: string[];\n /** Strategy for merging updates: \"overwrite\" | \"merge\" | \"skip\" */\n mergeStrategy?: 'overwrite' | 'merge' | 'skip';\n /** Create backup files before updating existing files */\n backupBeforeUpdate?: boolean;\n /** Directory name for backup files (defaults to \".backups\") */\n backupDirectory?: string;\n /** SQL filter to apply when pulling records from database */\n filter?: string;\n /** Configuration for pulling related entities */\n relatedEntities?: Record<string, RelatedEntityConfig>;\n /** Fields to externalize to separate files with optional configuration */\n externalizeFields?: string[] | {\n [fieldName: string]: {\n /** File extension to use (e.g., \".md\", \".txt\", \".html\") */\n extension?: string;\n }\n } | Array<{\n /** Field name to externalize */\n field: string;\n /** Pattern for the output file. Supports placeholders:\n * - {Name}: Entity's name field value\n * - {ID}: Entity's ID\n * - {FieldName}: The field being externalized\n * - Any other {FieldName} from the entity\n * Example: \"@file:templates/{Name}.template.md\"\n */\n pattern: string;\n }>;\n /** Fields to exclude from the pulled data (e.g., [\"TemplateID\"]) */\n excludeFields?: string[];\n /** Foreign key fields to convert to @lookup references */\n lookupFields?: {\n /** Field name in this entity (e.g., \"CategoryID\") */\n [fieldName: string]: {\n /** Target entity name (e.g., \"AI Prompt Categories\") */\n entity: string;\n /** Field in target entity to use for lookup (e.g., \"Name\") */\n field: string;\n };\n };\n };\n}\n\n/**\n * Folder-level configuration\n * \n * Defines default values that cascade down to all subdirectories. Stored in\n * .mj-folder.json files. Child folders can override parent defaults.\n */\nexport interface FolderConfig {\n /** Default field values that apply to all entities in this folder and subfolders */\n defaults: Record<string, any>;\n}\n\n/**\n * Load MemberJunction configuration from the filesystem\n * \n * Searches for mj.config.cjs starting from the current directory and walking up\n * the directory tree. Uses cosmiconfig for flexible configuration loading.\n * \n * @returns MJConfig object if found, null if not found or invalid\n * \n * @example\n * ```typescript\n * const config = loadMJConfig();\n * if (config) {\n * console.log(`Connecting to ${config.dbHost}:${config.dbPort || 1433}`);\n * }\n * ```\n */\nexport function loadMJConfig(): MJConfig | null {\n return configManager.loadMJConfig();\n}\n\n/**\n * Load sync configuration from a directory\n * \n * Loads .mj-sync.json file from the specified directory. This file can contain\n * either global sync configuration (no entity field) or entity-specific\n * configuration (with entity field).\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to SyncConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const syncConfig = await loadSyncConfig('/path/to/project');\n * if (syncConfig?.push?.requireConfirmation) {\n * // Show confirmation prompt\n * }\n * ```\n */\nexport async function loadSyncConfig(dir: string): Promise<SyncConfig | null> {\n const configPath = path.join(dir, '.mj-sync.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error('Error loading sync config:', error);\n return null;\n }\n }\n \n return null;\n}\n\n/**\n * Load entity-specific configuration from a directory\n * \n * Loads .mj-sync.json file that contains an \"entity\" field, indicating this\n * directory contains data for a specific entity type. Returns null if the\n * file doesn't exist or doesn't contain an entity field.\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to EntityConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const entityConfig = await loadEntityConfig('./ai-prompts');\n * if (entityConfig) {\n * console.log(`Directory contains ${entityConfig.entity} records`);\n * }\n * ```\n */\nexport async function loadEntityConfig(dir: string): Promise<EntityConfig | null> {\n const configPath = path.join(dir, '.mj-sync.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n const config = await fs.readJson(configPath);\n if (config.entity) {\n return config;\n }\n } catch (error) {\n console.error('Error loading entity config:', error);\n }\n }\n \n return null;\n}\n\n/**\n * Load folder-level configuration\n * \n * Loads .mj-folder.json file that contains default values to be applied to\n * all entities in this folder and its subfolders. Used for cascading defaults\n * in deep directory structures.\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to FolderConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const folderConfig = await loadFolderConfig('./templates');\n * if (folderConfig?.defaults) {\n * // Apply folder defaults to records\n * }\n * ```\n */\nexport async function loadFolderConfig(dir: string): Promise<FolderConfig | null> {\n const configPath = path.join(dir, '.mj-folder.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error('Error loading folder config:', error);\n return null;\n }\n }\n \n return null;\n}"]}
|
|
@@ -71,6 +71,8 @@ class PushService {
|
|
|
71
71
|
description: 'MetadataSync push operation',
|
|
72
72
|
statementTypes: "mutations",
|
|
73
73
|
prettyPrint: true,
|
|
74
|
+
filterPatterns: this.syncConfig.sqlLogging?.filterPatterns,
|
|
75
|
+
filterType: this.syncConfig.sqlLogging?.filterType,
|
|
74
76
|
});
|
|
75
77
|
if (options.verbose) {
|
|
76
78
|
callbacks?.onLog?.(`š SQL logging enabled: ${filepath}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PushService.js","sourceRoot":"","sources":["../../src/services/PushService.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs-extra';\nimport path from 'path';\nimport fastGlob from 'fast-glob';\nimport { BaseEntity, LogStatus, Metadata, UserInfo, CompositeKey } from '@memberjunction/core';\nimport { SyncEngine, RecordData } from '../lib/sync-engine';\nimport { loadEntityConfig, loadSyncConfig } from '../config';\nimport { FileBackupManager } from '../lib/file-backup-manager';\nimport { configManager } from '../lib/config-manager';\nimport { SQLLogger } from '../lib/sql-logger';\nimport { TransactionManager } from '../lib/transaction-manager';\nimport type { SqlLoggingSession, SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';\n\nexport interface PushOptions {\n dir?: string;\n dryRun?: boolean;\n verbose?: boolean;\n noValidate?: boolean;\n}\n\nexport interface PushCallbacks {\n onProgress?: (message: string) => void;\n onSuccess?: (message: string) => void;\n onError?: (message: string) => void;\n onWarn?: (message: string) => void;\n onLog?: (message: string) => void;\n onConfirm?: (message: string) => Promise<boolean>;\n}\n\nexport interface PushResult {\n created: number;\n updated: number;\n unchanged: number;\n errors: number;\n warnings: string[];\n sqlLogPath?: string;\n}\n\nexport interface EntityPushResult {\n created: number;\n updated: number;\n unchanged: number;\n errors: number;\n}\n\nexport class PushService {\n private syncEngine: SyncEngine;\n private contextUser: UserInfo;\n private warnings: string[] = [];\n private processedRecords: Map<string, { filePath: string; arrayIndex?: number; lineNumber?: number }> = new Map();\n private syncConfig: any;\n \n constructor(syncEngine: SyncEngine, contextUser: UserInfo) {\n this.syncEngine = syncEngine;\n this.contextUser = contextUser;\n }\n \n async push(options: PushOptions, callbacks?: PushCallbacks): Promise<PushResult> {\n this.warnings = [];\n this.processedRecords.clear();\n \n const fileBackupManager = new FileBackupManager();\n \n // Load sync config for SQL logging settings and autoCreateMissingRecords flag\n // If dir option is specified, load from that directory, otherwise use original CWD\n const configDir = options.dir ? path.resolve(configManager.getOriginalCwd(), options.dir) : configManager.getOriginalCwd();\n this.syncConfig = await loadSyncConfig(configDir);\n \n if (options.verbose) {\n callbacks?.onLog?.(`Original working directory: ${configManager.getOriginalCwd()}`);\n callbacks?.onLog?.(`Config directory (with dir option): ${configDir}`);\n callbacks?.onLog?.(`Config file path: ${path.join(configDir, '.mj-sync.json')}`);\n callbacks?.onLog?.(`Full sync config loaded: ${JSON.stringify(this.syncConfig, null, 2)}`);\n callbacks?.onLog?.(`SQL logging config: ${JSON.stringify(this.syncConfig?.sqlLogging)}`);\n }\n \n const sqlLogger = new SQLLogger(this.syncConfig);\n const transactionManager = new TransactionManager(sqlLogger);\n \n if (options.verbose) {\n callbacks?.onLog?.(`SQLLogger enabled status: ${sqlLogger.enabled}`);\n }\n \n // Setup SQL logging session with the provider if enabled\n let sqlLoggingSession: SqlLoggingSession | null = null;\n \n try {\n // Initialize SQL logger if enabled and not dry-run\n if (sqlLogger.enabled && !options.dryRun) {\n const provider = Metadata.Provider as SQLServerDataProvider;\n \n if (options.verbose) {\n callbacks?.onLog?.(`SQL logging enabled: ${sqlLogger.enabled}`);\n callbacks?.onLog?.(`Provider type: ${provider?.constructor?.name || 'Unknown'}`);\n callbacks?.onLog?.(`Has CreateSqlLogger: ${typeof provider?.CreateSqlLogger === 'function'}`);\n }\n \n if (provider && typeof provider.CreateSqlLogger === 'function') {\n // Generate filename with timestamp\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const filename = this.syncConfig.sqlLogging?.formatAsMigration \n ? `MetadataSync_Push_${timestamp}.sql`\n : `push_${timestamp}.sql`;\n \n // Use .sql-log-push directory in the config directory (where sync was initiated)\n const outputDir = path.join(configDir, this.syncConfig?.sqlLogging?.outputDirectory || './sql-log-push');\n const filepath = path.join(outputDir, filename);\n \n // Ensure the directory exists\n await fs.ensureDir(path.dirname(filepath));\n \n // Create the SQL logging session\n sqlLoggingSession = await provider.CreateSqlLogger(filepath, {\n formatAsMigration: this.syncConfig.sqlLogging?.formatAsMigration || false,\n description: 'MetadataSync push operation',\n statementTypes: \"mutations\",\n prettyPrint: true, \n });\n \n if (options.verbose) {\n callbacks?.onLog?.(`š SQL logging enabled: ${filepath}`);\n }\n } else {\n if (options.verbose) {\n callbacks?.onWarn?.('SQL logging requested but provider does not support it');\n }\n }\n }\n \n // Find entity directories to process\n // Note: If options.dir is specified, configDir already points to that directory\n // So we don't need to pass it as specificDir\n const entityDirs = this.findEntityDirectories(configDir, undefined, this.syncConfig?.directoryOrder);\n \n if (entityDirs.length === 0) {\n throw new Error('No entity directories found');\n }\n \n if (options.verbose) {\n callbacks?.onLog?.(`Found ${entityDirs.length} entity ${entityDirs.length === 1 ? 'directory' : 'directories'} to process`);\n }\n \n // Initialize file backup manager (unless in dry-run mode)\n if (!options.dryRun) {\n await fileBackupManager.initialize();\n if (options.verbose) {\n callbacks?.onLog?.('š File backup manager initialized');\n }\n }\n \n // Process each entity directory\n let totalCreated = 0;\n let totalUpdated = 0;\n let totalUnchanged = 0;\n let totalErrors = 0;\n \n // Begin transaction if not in dry-run mode\n if (!options.dryRun) {\n await transactionManager.beginTransaction();\n }\n \n try {\n for (const entityDir of entityDirs) {\n const entityConfig = await loadEntityConfig(entityDir);\n if (!entityConfig) {\n const warning = `Skipping ${entityDir} - no valid entity configuration`;\n this.warnings.push(warning);\n callbacks?.onWarn?.(warning);\n continue;\n }\n \n // Show folder with spinner at start\n const dirName = path.relative(process.cwd(), entityDir) || '.';\n callbacks?.onLog?.(`\\nš ${dirName}:`);\n \n // Use onProgress for animated spinner if available\n if (callbacks?.onProgress) {\n callbacks.onProgress(`Processing ${dirName}...`);\n } else {\n callbacks?.onLog?.(` ā³ Processing...`);\n }\n \n if (options.verbose && callbacks?.onLog) {\n callbacks.onLog(`Processing ${entityConfig.entity} in ${entityDir}`);\n }\n \n const result = await this.processEntityDirectory(\n entityDir,\n entityConfig,\n options,\n fileBackupManager,\n callbacks,\n sqlLogger\n );\n \n // Stop the spinner if we were using onProgress\n if (callbacks?.onProgress && callbacks?.onSuccess) {\n callbacks.onSuccess(`Processed ${dirName}`);\n }\n \n // Show per-directory summary\n const dirTotal = result.created + result.updated + result.unchanged;\n if (dirTotal > 0 || result.errors > 0) {\n callbacks?.onLog?.(` Total processed: ${dirTotal} unique records`);\n if (result.created > 0) {\n callbacks?.onLog?.(` ā Created: ${result.created}`);\n }\n if (result.updated > 0) {\n callbacks?.onLog?.(` ā Updated: ${result.updated}`);\n }\n if (result.unchanged > 0) {\n callbacks?.onLog?.(` - Unchanged: ${result.unchanged}`);\n }\n if (result.errors > 0) {\n callbacks?.onLog?.(` ā Errors: ${result.errors}`);\n }\n }\n \n totalCreated += result.created;\n totalUpdated += result.updated;\n totalUnchanged += result.unchanged;\n totalErrors += result.errors;\n }\n \n // Commit transaction if successful\n if (!options.dryRun && totalErrors === 0) {\n await transactionManager.commitTransaction();\n }\n } catch (error) {\n // Rollback transaction on error\n if (!options.dryRun) {\n await transactionManager.rollbackTransaction();\n }\n throw error;\n }\n \n // Commit file backups if successful and not in dry-run mode\n if (!options.dryRun && totalErrors === 0) {\n await fileBackupManager.cleanup();\n if (options.verbose) {\n callbacks?.onLog?.('ā
File backups committed');\n }\n }\n \n // Close SQL logging session if it was created\n let sqlLogPath: string | undefined;\n if (sqlLoggingSession) {\n sqlLogPath = sqlLoggingSession.filePath;\n await sqlLoggingSession.dispose();\n if (options.verbose) {\n callbacks?.onLog?.(`š SQL log written to: ${sqlLogPath}`);\n }\n }\n \n return {\n created: totalCreated,\n updated: totalUpdated,\n unchanged: totalUnchanged,\n errors: totalErrors,\n warnings: this.warnings,\n sqlLogPath\n };\n \n } catch (error) {\n // Rollback file backups on error\n if (!options.dryRun) {\n try {\n await fileBackupManager.rollback();\n callbacks?.onWarn?.('File backups rolled back due to error');\n } catch (rollbackError) {\n callbacks?.onWarn?.(`Failed to rollback file backups: ${rollbackError}`);\n }\n }\n \n // Close SQL logging session on error\n if (sqlLoggingSession) {\n try {\n await sqlLoggingSession.dispose();\n } catch (disposeError) {\n callbacks?.onWarn?.(`Failed to close SQL logging session: ${disposeError}`);\n }\n }\n \n throw error;\n }\n }\n \n private async processEntityDirectory(\n entityDir: string,\n entityConfig: any,\n options: PushOptions,\n fileBackupManager: FileBackupManager,\n callbacks?: PushCallbacks,\n sqlLogger?: SQLLogger\n ): Promise<EntityPushResult> {\n let created = 0;\n let updated = 0;\n let unchanged = 0;\n let errors = 0;\n \n // Find all JSON files in the directory\n const pattern = entityConfig.filePattern || '*.json';\n const files = await fastGlob(pattern, {\n cwd: entityDir,\n absolute: true,\n onlyFiles: true,\n dot: true,\n ignore: ['**/node_modules/**', '**/.mj-*.json']\n });\n \n if (options.verbose) {\n callbacks?.onLog?.(`Found ${files.length} files to process`);\n }\n \n // Process each file\n for (const filePath of files) {\n try {\n // Backup the file before any modifications (unless dry-run)\n if (!options.dryRun) {\n await fileBackupManager.backupFile(filePath);\n }\n \n const fileData = await fs.readJson(filePath);\n const records = Array.isArray(fileData) ? fileData : [fileData];\n const isArray = Array.isArray(fileData);\n \n for (let i = 0; i < records.length; i++) {\n const recordData = records[i];\n \n if (!this.isValidRecordData(recordData)) {\n callbacks?.onWarn?.(`Invalid record format in ${filePath}${isArray ? ` at index ${i}` : ''}`);\n errors++;\n continue;\n }\n \n try {\n // For arrays, work with a deep copy to avoid modifying the original\n const recordToProcess = isArray ? JSON.parse(JSON.stringify(recordData)) : recordData;\n \n const result = await this.processRecord(\n recordToProcess,\n entityConfig,\n entityDir,\n options,\n callbacks,\n filePath,\n isArray ? i : undefined\n );\n \n // Don't count duplicates in stats\n if (!result.isDuplicate) {\n if (result.status === 'created') created++;\n else if (result.status === 'updated') updated++;\n else if (result.status === 'unchanged') unchanged++;\n }\n \n // Add related entity stats\n created += result.relatedStats.created;\n updated += result.relatedStats.updated;\n unchanged += result.relatedStats.unchanged;\n \n // For arrays, update the original record's primaryKey, sync, and relatedEntities\n if (isArray) {\n // Update primaryKey if it exists (for new records)\n if (recordToProcess.primaryKey) {\n records[i].primaryKey = recordToProcess.primaryKey;\n }\n // Update sync metadata only if it was updated (dirty records only)\n if (recordToProcess.sync) {\n records[i].sync = recordToProcess.sync;\n }\n // Update relatedEntities to capture primaryKey/sync changes in nested entities\n if (recordToProcess.relatedEntities) {\n records[i].relatedEntities = recordToProcess.relatedEntities;\n }\n }\n \n // Record tracking is now handled inside processRecord\n \n } catch (recordError) {\n const errorMsg = `Error processing record in ${filePath}${isArray ? ` at index ${i}` : ''}: ${recordError}`;\n callbacks?.onError?.(errorMsg);\n errors++;\n }\n }\n \n // Write back the entire file if it's an array (after processing all records)\n if (isArray && !options.dryRun) {\n await fs.writeJson(filePath, records, { spaces: 2 });\n }\n } catch (fileError) {\n const errorMsg = `Error reading file ${filePath}: ${fileError}`;\n callbacks?.onError?.(errorMsg);\n errors++;\n }\n }\n \n return { created, updated, unchanged, errors };\n }\n \n private async processRecord(\n recordData: RecordData,\n entityConfig: any,\n entityDir: string,\n options: PushOptions,\n callbacks?: PushCallbacks,\n filePath?: string,\n arrayIndex?: number\n ): Promise<{ status: 'created' | 'updated' | 'unchanged' | 'error'; relatedStats: { created: number; updated: number; unchanged: number }; isDuplicate?: boolean }> {\n const metadata = new Metadata();\n \n // Get or create entity instance\n let entity = await metadata.GetEntityObject(entityConfig.entity, this.contextUser);\n if (!entity) {\n throw new Error(`Failed to create entity object for ${entityConfig.entity}`);\n }\n \n // Apply defaults from configuration\n const defaults = { ...entityConfig.defaults };\n \n // Build full record data - keep original values for file writing\n const originalFields = { ...recordData.fields };\n const fullData = {\n ...defaults,\n ...recordData.fields\n };\n \n // Process field values for database operations\n const processedData: Record<string, any> = {};\n for (const [fieldName, fieldValue] of Object.entries(fullData)) {\n const processedValue = await this.syncEngine.processFieldValue(\n fieldValue,\n entityDir,\n null, // parentRecord\n null // rootRecord\n );\n processedData[fieldName] = processedValue;\n }\n \n // Check if record exists\n const primaryKey = recordData.primaryKey;\n let exists = false;\n let isNew = false;\n \n if (primaryKey && Object.keys(primaryKey).length > 0) {\n // Try to load existing record\n const compositeKey = new CompositeKey();\n compositeKey.LoadFromSimpleObject(primaryKey);\n exists = await entity.InnerLoad(compositeKey);\n \n // Check autoCreateMissingRecords flag if record not found\n if (!exists) {\n const autoCreate = this.syncConfig?.push?.autoCreateMissingRecords ?? false;\n const pkDisplay = Object.entries(primaryKey)\n .map(([key, value]) => `${key}=${value}`)\n .join(', ');\n \n if (!autoCreate) {\n const warning = `Record not found: ${entityConfig.entity} with primaryKey {${pkDisplay}}. To auto-create missing records, set push.autoCreateMissingRecords=true in .mj-sync.json`;\n this.warnings.push(warning);\n callbacks?.onWarn?.(warning);\n return { status: 'error', relatedStats: { created: 0, updated: 0, unchanged: 0 } };\n } else if (options.verbose) {\n callbacks?.onLog?.(`Auto-creating missing ${entityConfig.entity} record with primaryKey {${pkDisplay}}`);\n }\n }\n }\n \n if (options.dryRun) {\n if (exists) {\n callbacks?.onLog?.(`[DRY RUN] Would update ${entityConfig.entity} record`);\n return { status: 'updated', relatedStats: { created: 0, updated: 0, unchanged: 0 } };\n } else {\n callbacks?.onLog?.(`[DRY RUN] Would create ${entityConfig.entity} record`);\n return { status: 'created', relatedStats: { created: 0, updated: 0, unchanged: 0 } };\n }\n }\n \n if (!exists) {\n entity.NewRecord(); // make sure our record starts out fresh\n isNew = true;\n \n // UUID generation now happens automatically in BaseEntity.NewRecord()\n \n // Set primary key values for new records if provided, this is important for the auto-create logic\n if (primaryKey) {\n for (const [pkField, pkValue] of Object.entries(primaryKey)) {\n entity.Set(pkField, pkValue);\n }\n }\n }\n \n // Set field values\n for (const [fieldName, fieldValue] of Object.entries(processedData)) {\n entity.Set(fieldName, fieldValue);\n }\n\n // Handle related entities\n if (recordData.relatedEntities) {\n // Store related entities to process after parent save\n (entity as any).__pendingRelatedEntities = recordData.relatedEntities;\n }\n \n // Check if the record is actually dirty before considering it changed\n let isDirty = entity.Dirty;\n \n // Also check if file content has changed (for @file references)\n if (!isDirty && !isNew && recordData.sync) {\n const currentChecksum = await this.syncEngine.calculateChecksumWithFileContent(originalFields, entityDir);\n if (currentChecksum !== recordData.sync.checksum) {\n isDirty = true;\n if (options.verbose) {\n callbacks?.onLog?.(`š File content changed for ${entityConfig.entity} record (checksum mismatch)`);\n }\n }\n }\n \n // If updating an existing record that's dirty, show what changed\n if (!isNew && isDirty) {\n const changes = entity.GetChangesSinceLastSave();\n const changeKeys = Object.keys(changes);\n if (changeKeys.length > 0) {\n // Get primary key info for display\n const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);\n const primaryKeyDisplay: string[] = [];\n if (entityInfo) {\n for (const pk of entityInfo.PrimaryKeys) {\n primaryKeyDisplay.push(`${pk.Name}: ${entity.Get(pk.Name)}`);\n }\n }\n \n callbacks?.onLog?.(`š Updating ${entityConfig.entity} record:`);\n if (primaryKeyDisplay.length > 0) {\n callbacks?.onLog?.(` Primary Key: ${primaryKeyDisplay.join(', ')}`);\n }\n callbacks?.onLog?.(` Changes:`);\n for (const fieldName of changeKeys) {\n const field = entity.GetFieldByName(fieldName);\n const oldValue = field ? field.OldValue : undefined;\n const newValue = (changes as any)[fieldName];\n callbacks?.onLog?.(` ${fieldName}: ${this.formatFieldValue(oldValue)} ā ${this.formatFieldValue(newValue)}`);\n }\n }\n }\n \n // Check for duplicate processing (but only for existing records that were loaded)\n let isDuplicate = false;\n if (!isNew && entity) {\n isDuplicate = this.checkAndTrackRecord(entityConfig.entity, entity, filePath, arrayIndex);\n }\n \n // Save the record (always call Save, but track if it was actually dirty)\n const saveResult = await entity.Save();\n \n if (!saveResult) {\n throw new Error(`Failed to save ${entityConfig.entity} record: ${entity.LatestResult?.Message || 'Unknown error'}`);\n }\n \n // Update primaryKey for new records\n if (isNew) {\n const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);\n if (entityInfo) {\n const newPrimaryKey: Record<string, any> = {};\n for (const pk of entityInfo.PrimaryKeys) {\n newPrimaryKey[pk.Name] = entity.Get(pk.Name);\n }\n recordData.primaryKey = newPrimaryKey;\n }\n \n // Track the new record now that we have its primary key\n this.checkAndTrackRecord(entityConfig.entity, entity, filePath, arrayIndex);\n }\n \n // Only update sync metadata if the record was actually dirty (changed)\n if (isNew || isDirty) {\n recordData.sync = {\n lastModified: new Date().toISOString(),\n checksum: await this.syncEngine.calculateChecksumWithFileContent(originalFields, entityDir)\n };\n }\n \n // Restore original field values to preserve @ references\n recordData.fields = originalFields;\n \n // Write back to file only if it's a single record (not part of an array)\n if (filePath && arrayIndex === undefined && !options.dryRun) {\n await fs.writeJson(filePath, recordData, { spaces: 2 });\n }\n \n // Process related entities after parent save\n let relatedStats = { created: 0, updated: 0, unchanged: 0 };\n if (recordData.relatedEntities && !options.dryRun) {\n relatedStats = await this.processRelatedEntities(\n recordData.relatedEntities,\n entity,\n entity, // root is same as parent for top level\n entityDir,\n options,\n callbacks,\n undefined, // fileBackupManager\n 1, // indentLevel\n filePath,\n arrayIndex\n );\n }\n \n // Store related stats on the result for propagation\n // Don't count duplicates in stats\n const status: 'created' | 'updated' | 'unchanged' = isDuplicate ? 'unchanged' : (isNew ? 'created' : (isDirty ? 'updated' : 'unchanged'));\n const result = {\n status,\n relatedStats,\n isDuplicate\n };\n \n // Return enhanced result with related stats\n return result;\n }\n \n private async processRelatedEntities(\n relatedEntities: Record<string, RecordData[]>,\n parentEntity: BaseEntity,\n rootEntity: BaseEntity,\n baseDir: string,\n options: PushOptions,\n callbacks?: PushCallbacks,\n fileBackupManager?: FileBackupManager,\n indentLevel: number = 1,\n parentFilePath?: string,\n parentArrayIndex?: number\n ): Promise<{ created: number; updated: number; unchanged: number }> {\n const indent = ' '.repeat(indentLevel);\n const stats = { created: 0, updated: 0, unchanged: 0 };\n \n for (const [entityName, records] of Object.entries(relatedEntities)) {\n if (options.verbose) {\n callbacks?.onLog?.(`${indent}ā³ Processing ${records.length} related ${entityName} records`);\n }\n \n for (const relatedRecord of records) {\n try {\n // Load or create entity\n let entity = null;\n let isNew = false;\n \n if (relatedRecord.primaryKey) {\n entity = await this.syncEngine.loadEntity(entityName, relatedRecord.primaryKey);\n \n // Warn if record has primaryKey but wasn't found\n if (!entity) {\n const pkDisplay = Object.entries(relatedRecord.primaryKey)\n .map(([key, value]) => `${key}=${value}`)\n .join(', ');\n \n // Load sync config to check autoCreateMissingRecords setting\n const autoCreate = this.syncConfig?.push?.autoCreateMissingRecords ?? false;\n \n if (!autoCreate) {\n const fileRef = parentFilePath ? path.relative(configManager.getOriginalCwd(), parentFilePath) : 'unknown';\n const warning = `${indent}ā ļø Related record not found: ${entityName} with primaryKey {${pkDisplay}} at ${fileRef}`;\n this.warnings.push(warning);\n callbacks?.onWarn?.(warning);\n const warning2 = `${indent} To auto-create missing records, set push.autoCreateMissingRecords=true in .mj-sync.json`;\n this.warnings.push(warning2);\n callbacks?.onWarn?.(warning2);\n \n // Skip this record\n continue;\n } else {\n if (options.verbose) {\n callbacks?.onLog?.(`${indent} Auto-creating missing related ${entityName} record with primaryKey {${pkDisplay}}`);\n }\n }\n }\n }\n \n if (!entity) {\n entity = await this.syncEngine.createEntityObject(entityName);\n entity.NewRecord();\n isNew = true;\n \n // Handle primary keys for new related entity records\n const entityInfo = this.syncEngine.getEntityInfo(entityName);\n if (entityInfo) {\n for (const pk of entityInfo.PrimaryKeys) {\n if (!pk.AutoIncrement) {\n // Check if we have a value in primaryKey object\n if (relatedRecord.primaryKey?.[pk.Name]) {\n // User specified a primary key for new record, set it on entity directly\n // Don't add to fields as it will be in primaryKey section\n (entity as any)[pk.Name] = relatedRecord.primaryKey[pk.Name];\n if (options.verbose) {\n callbacks?.onLog?.(`${indent} Using specified primary key ${pk.Name}: ${relatedRecord.primaryKey[pk.Name]}`);\n }\n }\n // Note: BaseEntity.NewRecord() automatically generates UUIDs for uniqueidentifier primary keys\n }\n }\n }\n }\n \n // Apply fields with parent/root context\n for (const [field, value] of Object.entries(relatedRecord.fields)) {\n if (field in entity) {\n try {\n const processedValue = await this.syncEngine.processFieldValue(\n value, \n baseDir, \n parentEntity, \n rootEntity\n );\n if (options.verbose) {\n callbacks?.onLog?.(`${indent} Setting ${field}: ${this.formatFieldValue(value)} -> ${this.formatFieldValue(processedValue)}`);\n }\n (entity as any)[field] = processedValue;\n } catch (error) {\n throw new Error(`Failed to process field '${field}' in ${entityName}: ${error}`);\n }\n } else {\n const warning = `${indent} Field '${field}' does not exist on entity '${entityName}'`;\n this.warnings.push(warning);\n callbacks?.onWarn?.(warning);\n }\n }\n \n // Check for duplicate processing (but only for existing records that were loaded)\n let isDuplicate = false;\n if (!isNew && entity) {\n // Use parent file path for related entities since they're defined in the parent's file\n const relatedFilePath = parentFilePath || path.join(baseDir, 'unknown');\n isDuplicate = this.checkAndTrackRecord(entityName, entity, relatedFilePath, parentArrayIndex);\n }\n \n // Check if the record is dirty before saving\n let wasActuallyUpdated = false;\n if (!isNew && entity.Dirty) {\n // Record is dirty, get the changes\n const changes = entity.GetChangesSinceLastSave();\n const changeKeys = Object.keys(changes);\n if (changeKeys.length > 0) {\n wasActuallyUpdated = true;\n \n // Get primary key info for display\n const entityInfo = this.syncEngine.getEntityInfo(entityName);\n const primaryKeyDisplay: string[] = [];\n if (entityInfo) {\n for (const pk of entityInfo.PrimaryKeys) {\n primaryKeyDisplay.push(`${pk.Name}: ${entity.Get(pk.Name)}`);\n }\n }\n \n callbacks?.onLog?.(''); // Add newline before update output\n callbacks?.onLog?.(`${indent}š Updating related ${entityName} record:`);\n if (primaryKeyDisplay.length > 0) {\n callbacks?.onLog?.(`${indent} Primary Key: ${primaryKeyDisplay.join(', ')}`);\n }\n callbacks?.onLog?.(`${indent} Changes:`);\n for (const fieldName of changeKeys) {\n const field = entity.GetFieldByName(fieldName);\n const oldValue = field ? field.OldValue : undefined;\n const newValue = (changes as any)[fieldName];\n callbacks?.onLog?.(`${indent} ${fieldName}: ${this.formatFieldValue(oldValue)} ā ${this.formatFieldValue(newValue)}`);\n }\n }\n } else if (isNew) {\n wasActuallyUpdated = true;\n }\n \n // Save the related entity\n const saved = await entity.Save();\n if (!saved) {\n const message = entity.LatestResult?.Message;\n if (message) {\n throw new Error(`Failed to save related ${entityName}: ${message}`);\n }\n \n const errors = entity.LatestResult?.Errors?.map(err => \n typeof err === 'string' ? err : (err?.message || JSON.stringify(err))\n )?.join(', ') || 'Unknown error';\n throw new Error(`Failed to save related ${entityName}: ${errors}`);\n }\n \n // Update stats - don't count duplicates\n if (!isDuplicate) {\n if (isNew) {\n stats.created++;\n } else if (wasActuallyUpdated) {\n stats.updated++;\n } else {\n stats.unchanged++;\n }\n }\n \n if (options.verbose && wasActuallyUpdated) {\n callbacks?.onLog?.(`${indent} ā ${isNew ? 'Created' : 'Updated'} ${entityName} record`);\n } else if (options.verbose && !wasActuallyUpdated) {\n callbacks?.onLog?.(`${indent} - No changes to ${entityName} record`);\n }\n \n // Update the related record with primary key and sync metadata\n const entityInfo = this.syncEngine.getEntityInfo(entityName);\n if (entityInfo) {\n // Update primary key if new\n if (isNew) {\n relatedRecord.primaryKey = {};\n for (const pk of entityInfo.PrimaryKeys) {\n relatedRecord.primaryKey[pk.Name] = entity.Get(pk.Name);\n }\n \n // Track the new related entity now that we have its primary key\n const relatedFilePath = parentFilePath || path.join(baseDir, 'unknown');\n this.checkAndTrackRecord(entityName, entity, relatedFilePath, parentArrayIndex);\n }\n \n // Always update sync metadata\n relatedRecord.sync = {\n lastModified: new Date().toISOString(),\n checksum: this.syncEngine.calculateChecksum(relatedRecord.fields)\n };\n }\n \n // Process nested related entities if any\n if (relatedRecord.relatedEntities) {\n const nestedStats = await this.processRelatedEntities(\n relatedRecord.relatedEntities,\n entity,\n rootEntity,\n baseDir,\n options,\n callbacks,\n fileBackupManager,\n indentLevel + 1,\n parentFilePath,\n parentArrayIndex\n );\n \n // Accumulate nested stats\n stats.created += nestedStats.created;\n stats.updated += nestedStats.updated;\n stats.unchanged += nestedStats.unchanged;\n }\n } catch (error) {\n throw new Error(`Failed to process related ${entityName}: ${error}`);\n }\n }\n }\n \n return stats;\n }\n \n private isValidRecordData(data: any): data is RecordData {\n return data && \n typeof data === 'object' && \n 'fields' in data &&\n typeof data.fields === 'object';\n }\n \n private getRecordKey(recordData: RecordData, entityName: string): string {\n if (recordData.primaryKey) {\n const keys = Object.entries(recordData.primaryKey)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}:${v}`)\n .join('|');\n return `${entityName}|${keys}`;\n }\n \n // Generate a key from fields if no primary key\n const fieldKeys = Object.keys(recordData.fields).sort().join(',');\n return `${entityName}|fields:${fieldKeys}`;\n }\n \n private formatFieldValue(value: any, maxLength: number = 50): string {\n let strValue = JSON.stringify(value);\n strValue = strValue.trim();\n\n if (strValue.length > maxLength) {\n return strValue.substring(0, maxLength) + '...';\n }\n\n return strValue;\n }\n \n /**\n * Generate a unique tracking key for a record based on entity name and primary key values\n */\n private generateRecordKey(entityName: string, entity: BaseEntity): string {\n // Use the built-in CompositeKey ToURLSegment method\n const keySegment = entity.PrimaryKey.ToURLSegment();\n return `${entityName}|${keySegment}`;\n }\n \n /**\n * Check if a record has already been processed and warn if duplicate\n */\n private checkAndTrackRecord(\n entityName: string, \n entity: BaseEntity, \n filePath?: string,\n arrayIndex?: number,\n lineNumber?: number\n ): boolean {\n const recordKey = this.generateRecordKey(entityName, entity);\n \n const existing = this.processedRecords.get(recordKey);\n if (existing) {\n const primaryKeyDisplay = entity.EntityInfo?.PrimaryKeys\n .map(pk => `${pk.Name}: ${entity.Get(pk.Name)}`)\n .join(', ') || 'unknown';\n \n // Format file location with clickable link for VSCode\n // Create maps with just the line numbers we have\n const currentLineMap = lineNumber ? new Map([[arrayIndex || 0, lineNumber]]) : undefined;\n const originalLineMap = existing.lineNumber ? new Map([[existing.arrayIndex || 0, existing.lineNumber]]) : undefined;\n \n const currentLocation = this.formatFileLocation(filePath, arrayIndex, currentLineMap);\n const originalLocation = this.formatFileLocation(existing.filePath, existing.arrayIndex, originalLineMap);\n \n const warning = `ā ļø Duplicate record detected for ${entityName} (${primaryKeyDisplay})`;\n this.warnings.push(warning);\n const warning2 = ` Current location: ${currentLocation}`;\n this.warnings.push(warning2);\n const warning3 = ` Original location: ${originalLocation}`;\n this.warnings.push(warning3);\n const warning4 = ` The duplicate update will proceed, but you should review your data for unintended duplicates.`;\n this.warnings.push(warning4);\n \n return true; // is duplicate\n }\n \n // Track the record with its source location\n this.processedRecords.set(recordKey, {\n filePath: filePath || 'unknown',\n arrayIndex,\n lineNumber\n });\n return false; // not duplicate\n }\n \n /**\n * Format file location with clickable link for VSCode\n */\n private formatFileLocation(filePath?: string, arrayIndex?: number, lineNumbers?: Map<number, number>): string {\n if (!filePath || filePath === 'unknown') {\n return 'unknown';\n }\n \n // Get absolute path for better VSCode integration\n const absolutePath = path.resolve(filePath);\n \n // Try to get actual line number from our tracking\n let lineNumber = 1;\n if (arrayIndex !== undefined && lineNumbers && lineNumbers.has(arrayIndex)) {\n lineNumber = lineNumbers.get(arrayIndex)!;\n } else if (arrayIndex !== undefined) {\n // Fallback estimation if we don't have actual line numbers\n lineNumber = 2 + (arrayIndex * 15);\n }\n \n // Create clickable file path for VSCode - format: file:line\n // VSCode will make this clickable in the terminal\n return `${absolutePath}:${lineNumber}`;\n }\n \n private findEntityDirectories(baseDir: string, specificDir?: string, directoryOrder?: string[]): string[] {\n const dirs: string[] = [];\n \n if (specificDir) {\n // Process specific directory\n const fullPath = path.resolve(baseDir, specificDir);\n if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {\n // Check if this directory has an entity configuration\n const configPath = path.join(fullPath, '.mj-sync.json');\n if (fs.existsSync(configPath)) {\n try {\n const config = fs.readJsonSync(configPath);\n if (config.entity) {\n // It's an entity directory, add it\n dirs.push(fullPath);\n } else {\n // It's a container directory, search its subdirectories\n this.findEntityDirectoriesRecursive(fullPath, dirs);\n }\n } catch {\n // Invalid config, skip\n }\n }\n }\n } else {\n // Find all entity directories\n this.findEntityDirectoriesRecursive(baseDir, dirs);\n }\n \n // Apply directory ordering if specified\n if (directoryOrder && directoryOrder.length > 0 && !specificDir) {\n // Create a map of directory name to order index\n const orderMap = new Map<string, number>();\n directoryOrder.forEach((dir, index) => {\n orderMap.set(dir, index);\n });\n \n // Sort directories based on the order map\n dirs.sort((a, b) => {\n const nameA = path.basename(a);\n const nameB = path.basename(b);\n const orderA = orderMap.get(nameA) ?? Number.MAX_SAFE_INTEGER;\n const orderB = orderMap.get(nameB) ?? Number.MAX_SAFE_INTEGER;\n \n // If both have specified orders, use them\n if (orderA !== Number.MAX_SAFE_INTEGER || orderB !== Number.MAX_SAFE_INTEGER) {\n return orderA - orderB;\n }\n \n // Otherwise, maintain original order (stable sort)\n return 0;\n });\n }\n \n return dirs;\n }\n\n private findEntityDirectoriesRecursive(dir: string, dirs: string[]): void {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {\n const fullPath = path.join(dir, entry.name);\n const configPath = path.join(fullPath, '.mj-sync.json');\n \n if (fs.existsSync(configPath)) {\n try {\n const config = fs.readJsonSync(configPath);\n if (config.entity) {\n dirs.push(fullPath);\n }\n } catch {\n // Skip invalid config files\n }\n } else {\n // Recurse into subdirectories\n this.findEntityDirectoriesRecursive(fullPath, dirs);\n }\n }\n }\n }\n}"]}
|
|
1
|
+
{"version":3,"file":"PushService.js","sourceRoot":"","sources":["../../src/services/PushService.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs-extra';\nimport path from 'path';\nimport fastGlob from 'fast-glob';\nimport { BaseEntity, LogStatus, Metadata, UserInfo, CompositeKey } from '@memberjunction/core';\nimport { SyncEngine, RecordData } from '../lib/sync-engine';\nimport { loadEntityConfig, loadSyncConfig } from '../config';\nimport { FileBackupManager } from '../lib/file-backup-manager';\nimport { configManager } from '../lib/config-manager';\nimport { SQLLogger } from '../lib/sql-logger';\nimport { TransactionManager } from '../lib/transaction-manager';\nimport type { SqlLoggingSession, SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';\n\nexport interface PushOptions {\n dir?: string;\n dryRun?: boolean;\n verbose?: boolean;\n noValidate?: boolean;\n}\n\nexport interface PushCallbacks {\n onProgress?: (message: string) => void;\n onSuccess?: (message: string) => void;\n onError?: (message: string) => void;\n onWarn?: (message: string) => void;\n onLog?: (message: string) => void;\n onConfirm?: (message: string) => Promise<boolean>;\n}\n\nexport interface PushResult {\n created: number;\n updated: number;\n unchanged: number;\n errors: number;\n warnings: string[];\n sqlLogPath?: string;\n}\n\nexport interface EntityPushResult {\n created: number;\n updated: number;\n unchanged: number;\n errors: number;\n}\n\nexport class PushService {\n private syncEngine: SyncEngine;\n private contextUser: UserInfo;\n private warnings: string[] = [];\n private processedRecords: Map<string, { filePath: string; arrayIndex?: number; lineNumber?: number }> = new Map();\n private syncConfig: any;\n \n constructor(syncEngine: SyncEngine, contextUser: UserInfo) {\n this.syncEngine = syncEngine;\n this.contextUser = contextUser;\n }\n \n async push(options: PushOptions, callbacks?: PushCallbacks): Promise<PushResult> {\n this.warnings = [];\n this.processedRecords.clear();\n \n const fileBackupManager = new FileBackupManager();\n \n // Load sync config for SQL logging settings and autoCreateMissingRecords flag\n // If dir option is specified, load from that directory, otherwise use original CWD\n const configDir = options.dir ? path.resolve(configManager.getOriginalCwd(), options.dir) : configManager.getOriginalCwd();\n this.syncConfig = await loadSyncConfig(configDir);\n \n if (options.verbose) {\n callbacks?.onLog?.(`Original working directory: ${configManager.getOriginalCwd()}`);\n callbacks?.onLog?.(`Config directory (with dir option): ${configDir}`);\n callbacks?.onLog?.(`Config file path: ${path.join(configDir, '.mj-sync.json')}`);\n callbacks?.onLog?.(`Full sync config loaded: ${JSON.stringify(this.syncConfig, null, 2)}`);\n callbacks?.onLog?.(`SQL logging config: ${JSON.stringify(this.syncConfig?.sqlLogging)}`);\n }\n \n const sqlLogger = new SQLLogger(this.syncConfig);\n const transactionManager = new TransactionManager(sqlLogger);\n \n if (options.verbose) {\n callbacks?.onLog?.(`SQLLogger enabled status: ${sqlLogger.enabled}`);\n }\n \n // Setup SQL logging session with the provider if enabled\n let sqlLoggingSession: SqlLoggingSession | null = null;\n \n try {\n // Initialize SQL logger if enabled and not dry-run\n if (sqlLogger.enabled && !options.dryRun) {\n const provider = Metadata.Provider as SQLServerDataProvider;\n \n if (options.verbose) {\n callbacks?.onLog?.(`SQL logging enabled: ${sqlLogger.enabled}`);\n callbacks?.onLog?.(`Provider type: ${provider?.constructor?.name || 'Unknown'}`);\n callbacks?.onLog?.(`Has CreateSqlLogger: ${typeof provider?.CreateSqlLogger === 'function'}`);\n }\n \n if (provider && typeof provider.CreateSqlLogger === 'function') {\n // Generate filename with timestamp\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const filename = this.syncConfig.sqlLogging?.formatAsMigration \n ? `MetadataSync_Push_${timestamp}.sql`\n : `push_${timestamp}.sql`;\n \n // Use .sql-log-push directory in the config directory (where sync was initiated)\n const outputDir = path.join(configDir, this.syncConfig?.sqlLogging?.outputDirectory || './sql-log-push');\n const filepath = path.join(outputDir, filename);\n \n // Ensure the directory exists\n await fs.ensureDir(path.dirname(filepath));\n \n // Create the SQL logging session\n sqlLoggingSession = await provider.CreateSqlLogger(filepath, {\n formatAsMigration: this.syncConfig.sqlLogging?.formatAsMigration || false,\n description: 'MetadataSync push operation',\n statementTypes: \"mutations\",\n prettyPrint: true,\n filterPatterns: this.syncConfig.sqlLogging?.filterPatterns,\n filterType: this.syncConfig.sqlLogging?.filterType,\n });\n \n if (options.verbose) {\n callbacks?.onLog?.(`š SQL logging enabled: ${filepath}`);\n }\n } else {\n if (options.verbose) {\n callbacks?.onWarn?.('SQL logging requested but provider does not support it');\n }\n }\n }\n \n // Find entity directories to process\n // Note: If options.dir is specified, configDir already points to that directory\n // So we don't need to pass it as specificDir\n const entityDirs = this.findEntityDirectories(configDir, undefined, this.syncConfig?.directoryOrder);\n \n if (entityDirs.length === 0) {\n throw new Error('No entity directories found');\n }\n \n if (options.verbose) {\n callbacks?.onLog?.(`Found ${entityDirs.length} entity ${entityDirs.length === 1 ? 'directory' : 'directories'} to process`);\n }\n \n // Initialize file backup manager (unless in dry-run mode)\n if (!options.dryRun) {\n await fileBackupManager.initialize();\n if (options.verbose) {\n callbacks?.onLog?.('š File backup manager initialized');\n }\n }\n \n // Process each entity directory\n let totalCreated = 0;\n let totalUpdated = 0;\n let totalUnchanged = 0;\n let totalErrors = 0;\n \n // Begin transaction if not in dry-run mode\n if (!options.dryRun) {\n await transactionManager.beginTransaction();\n }\n \n try {\n for (const entityDir of entityDirs) {\n const entityConfig = await loadEntityConfig(entityDir);\n if (!entityConfig) {\n const warning = `Skipping ${entityDir} - no valid entity configuration`;\n this.warnings.push(warning);\n callbacks?.onWarn?.(warning);\n continue;\n }\n \n // Show folder with spinner at start\n const dirName = path.relative(process.cwd(), entityDir) || '.';\n callbacks?.onLog?.(`\\nš ${dirName}:`);\n \n // Use onProgress for animated spinner if available\n if (callbacks?.onProgress) {\n callbacks.onProgress(`Processing ${dirName}...`);\n } else {\n callbacks?.onLog?.(` ā³ Processing...`);\n }\n \n if (options.verbose && callbacks?.onLog) {\n callbacks.onLog(`Processing ${entityConfig.entity} in ${entityDir}`);\n }\n \n const result = await this.processEntityDirectory(\n entityDir,\n entityConfig,\n options,\n fileBackupManager,\n callbacks,\n sqlLogger\n );\n \n // Stop the spinner if we were using onProgress\n if (callbacks?.onProgress && callbacks?.onSuccess) {\n callbacks.onSuccess(`Processed ${dirName}`);\n }\n \n // Show per-directory summary\n const dirTotal = result.created + result.updated + result.unchanged;\n if (dirTotal > 0 || result.errors > 0) {\n callbacks?.onLog?.(` Total processed: ${dirTotal} unique records`);\n if (result.created > 0) {\n callbacks?.onLog?.(` ā Created: ${result.created}`);\n }\n if (result.updated > 0) {\n callbacks?.onLog?.(` ā Updated: ${result.updated}`);\n }\n if (result.unchanged > 0) {\n callbacks?.onLog?.(` - Unchanged: ${result.unchanged}`);\n }\n if (result.errors > 0) {\n callbacks?.onLog?.(` ā Errors: ${result.errors}`);\n }\n }\n \n totalCreated += result.created;\n totalUpdated += result.updated;\n totalUnchanged += result.unchanged;\n totalErrors += result.errors;\n }\n \n // Commit transaction if successful\n if (!options.dryRun && totalErrors === 0) {\n await transactionManager.commitTransaction();\n }\n } catch (error) {\n // Rollback transaction on error\n if (!options.dryRun) {\n await transactionManager.rollbackTransaction();\n }\n throw error;\n }\n \n // Commit file backups if successful and not in dry-run mode\n if (!options.dryRun && totalErrors === 0) {\n await fileBackupManager.cleanup();\n if (options.verbose) {\n callbacks?.onLog?.('ā
File backups committed');\n }\n }\n \n // Close SQL logging session if it was created\n let sqlLogPath: string | undefined;\n if (sqlLoggingSession) {\n sqlLogPath = sqlLoggingSession.filePath;\n await sqlLoggingSession.dispose();\n if (options.verbose) {\n callbacks?.onLog?.(`š SQL log written to: ${sqlLogPath}`);\n }\n }\n \n return {\n created: totalCreated,\n updated: totalUpdated,\n unchanged: totalUnchanged,\n errors: totalErrors,\n warnings: this.warnings,\n sqlLogPath\n };\n \n } catch (error) {\n // Rollback file backups on error\n if (!options.dryRun) {\n try {\n await fileBackupManager.rollback();\n callbacks?.onWarn?.('File backups rolled back due to error');\n } catch (rollbackError) {\n callbacks?.onWarn?.(`Failed to rollback file backups: ${rollbackError}`);\n }\n }\n \n // Close SQL logging session on error\n if (sqlLoggingSession) {\n try {\n await sqlLoggingSession.dispose();\n } catch (disposeError) {\n callbacks?.onWarn?.(`Failed to close SQL logging session: ${disposeError}`);\n }\n }\n \n throw error;\n }\n }\n \n private async processEntityDirectory(\n entityDir: string,\n entityConfig: any,\n options: PushOptions,\n fileBackupManager: FileBackupManager,\n callbacks?: PushCallbacks,\n sqlLogger?: SQLLogger\n ): Promise<EntityPushResult> {\n let created = 0;\n let updated = 0;\n let unchanged = 0;\n let errors = 0;\n \n // Find all JSON files in the directory\n const pattern = entityConfig.filePattern || '*.json';\n const files = await fastGlob(pattern, {\n cwd: entityDir,\n absolute: true,\n onlyFiles: true,\n dot: true,\n ignore: ['**/node_modules/**', '**/.mj-*.json']\n });\n \n if (options.verbose) {\n callbacks?.onLog?.(`Found ${files.length} files to process`);\n }\n \n // Process each file\n for (const filePath of files) {\n try {\n // Backup the file before any modifications (unless dry-run)\n if (!options.dryRun) {\n await fileBackupManager.backupFile(filePath);\n }\n \n const fileData = await fs.readJson(filePath);\n const records = Array.isArray(fileData) ? fileData : [fileData];\n const isArray = Array.isArray(fileData);\n \n for (let i = 0; i < records.length; i++) {\n const recordData = records[i];\n \n if (!this.isValidRecordData(recordData)) {\n callbacks?.onWarn?.(`Invalid record format in ${filePath}${isArray ? ` at index ${i}` : ''}`);\n errors++;\n continue;\n }\n \n try {\n // For arrays, work with a deep copy to avoid modifying the original\n const recordToProcess = isArray ? JSON.parse(JSON.stringify(recordData)) : recordData;\n \n const result = await this.processRecord(\n recordToProcess,\n entityConfig,\n entityDir,\n options,\n callbacks,\n filePath,\n isArray ? i : undefined\n );\n \n // Don't count duplicates in stats\n if (!result.isDuplicate) {\n if (result.status === 'created') created++;\n else if (result.status === 'updated') updated++;\n else if (result.status === 'unchanged') unchanged++;\n }\n \n // Add related entity stats\n created += result.relatedStats.created;\n updated += result.relatedStats.updated;\n unchanged += result.relatedStats.unchanged;\n \n // For arrays, update the original record's primaryKey, sync, and relatedEntities\n if (isArray) {\n // Update primaryKey if it exists (for new records)\n if (recordToProcess.primaryKey) {\n records[i].primaryKey = recordToProcess.primaryKey;\n }\n // Update sync metadata only if it was updated (dirty records only)\n if (recordToProcess.sync) {\n records[i].sync = recordToProcess.sync;\n }\n // Update relatedEntities to capture primaryKey/sync changes in nested entities\n if (recordToProcess.relatedEntities) {\n records[i].relatedEntities = recordToProcess.relatedEntities;\n }\n }\n \n // Record tracking is now handled inside processRecord\n \n } catch (recordError) {\n const errorMsg = `Error processing record in ${filePath}${isArray ? ` at index ${i}` : ''}: ${recordError}`;\n callbacks?.onError?.(errorMsg);\n errors++;\n }\n }\n \n // Write back the entire file if it's an array (after processing all records)\n if (isArray && !options.dryRun) {\n await fs.writeJson(filePath, records, { spaces: 2 });\n }\n } catch (fileError) {\n const errorMsg = `Error reading file ${filePath}: ${fileError}`;\n callbacks?.onError?.(errorMsg);\n errors++;\n }\n }\n \n return { created, updated, unchanged, errors };\n }\n \n private async processRecord(\n recordData: RecordData,\n entityConfig: any,\n entityDir: string,\n options: PushOptions,\n callbacks?: PushCallbacks,\n filePath?: string,\n arrayIndex?: number\n ): Promise<{ status: 'created' | 'updated' | 'unchanged' | 'error'; relatedStats: { created: number; updated: number; unchanged: number }; isDuplicate?: boolean }> {\n const metadata = new Metadata();\n \n // Get or create entity instance\n let entity = await metadata.GetEntityObject(entityConfig.entity, this.contextUser);\n if (!entity) {\n throw new Error(`Failed to create entity object for ${entityConfig.entity}`);\n }\n \n // Apply defaults from configuration\n const defaults = { ...entityConfig.defaults };\n \n // Build full record data - keep original values for file writing\n const originalFields = { ...recordData.fields };\n const fullData = {\n ...defaults,\n ...recordData.fields\n };\n \n // Process field values for database operations\n const processedData: Record<string, any> = {};\n for (const [fieldName, fieldValue] of Object.entries(fullData)) {\n const processedValue = await this.syncEngine.processFieldValue(\n fieldValue,\n entityDir,\n null, // parentRecord\n null // rootRecord\n );\n processedData[fieldName] = processedValue;\n }\n \n // Check if record exists\n const primaryKey = recordData.primaryKey;\n let exists = false;\n let isNew = false;\n \n if (primaryKey && Object.keys(primaryKey).length > 0) {\n // Try to load existing record\n const compositeKey = new CompositeKey();\n compositeKey.LoadFromSimpleObject(primaryKey);\n exists = await entity.InnerLoad(compositeKey);\n \n // Check autoCreateMissingRecords flag if record not found\n if (!exists) {\n const autoCreate = this.syncConfig?.push?.autoCreateMissingRecords ?? false;\n const pkDisplay = Object.entries(primaryKey)\n .map(([key, value]) => `${key}=${value}`)\n .join(', ');\n \n if (!autoCreate) {\n const warning = `Record not found: ${entityConfig.entity} with primaryKey {${pkDisplay}}. To auto-create missing records, set push.autoCreateMissingRecords=true in .mj-sync.json`;\n this.warnings.push(warning);\n callbacks?.onWarn?.(warning);\n return { status: 'error', relatedStats: { created: 0, updated: 0, unchanged: 0 } };\n } else if (options.verbose) {\n callbacks?.onLog?.(`Auto-creating missing ${entityConfig.entity} record with primaryKey {${pkDisplay}}`);\n }\n }\n }\n \n if (options.dryRun) {\n if (exists) {\n callbacks?.onLog?.(`[DRY RUN] Would update ${entityConfig.entity} record`);\n return { status: 'updated', relatedStats: { created: 0, updated: 0, unchanged: 0 } };\n } else {\n callbacks?.onLog?.(`[DRY RUN] Would create ${entityConfig.entity} record`);\n return { status: 'created', relatedStats: { created: 0, updated: 0, unchanged: 0 } };\n }\n }\n \n if (!exists) {\n entity.NewRecord(); // make sure our record starts out fresh\n isNew = true;\n \n // UUID generation now happens automatically in BaseEntity.NewRecord()\n \n // Set primary key values for new records if provided, this is important for the auto-create logic\n if (primaryKey) {\n for (const [pkField, pkValue] of Object.entries(primaryKey)) {\n entity.Set(pkField, pkValue);\n }\n }\n }\n \n // Set field values\n for (const [fieldName, fieldValue] of Object.entries(processedData)) {\n entity.Set(fieldName, fieldValue);\n }\n\n // Handle related entities\n if (recordData.relatedEntities) {\n // Store related entities to process after parent save\n (entity as any).__pendingRelatedEntities = recordData.relatedEntities;\n }\n \n // Check if the record is actually dirty before considering it changed\n let isDirty = entity.Dirty;\n \n // Also check if file content has changed (for @file references)\n if (!isDirty && !isNew && recordData.sync) {\n const currentChecksum = await this.syncEngine.calculateChecksumWithFileContent(originalFields, entityDir);\n if (currentChecksum !== recordData.sync.checksum) {\n isDirty = true;\n if (options.verbose) {\n callbacks?.onLog?.(`š File content changed for ${entityConfig.entity} record (checksum mismatch)`);\n }\n }\n }\n \n // If updating an existing record that's dirty, show what changed\n if (!isNew && isDirty) {\n const changes = entity.GetChangesSinceLastSave();\n const changeKeys = Object.keys(changes);\n if (changeKeys.length > 0) {\n // Get primary key info for display\n const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);\n const primaryKeyDisplay: string[] = [];\n if (entityInfo) {\n for (const pk of entityInfo.PrimaryKeys) {\n primaryKeyDisplay.push(`${pk.Name}: ${entity.Get(pk.Name)}`);\n }\n }\n \n callbacks?.onLog?.(`š Updating ${entityConfig.entity} record:`);\n if (primaryKeyDisplay.length > 0) {\n callbacks?.onLog?.(` Primary Key: ${primaryKeyDisplay.join(', ')}`);\n }\n callbacks?.onLog?.(` Changes:`);\n for (const fieldName of changeKeys) {\n const field = entity.GetFieldByName(fieldName);\n const oldValue = field ? field.OldValue : undefined;\n const newValue = (changes as any)[fieldName];\n callbacks?.onLog?.(` ${fieldName}: ${this.formatFieldValue(oldValue)} ā ${this.formatFieldValue(newValue)}`);\n }\n }\n }\n \n // Check for duplicate processing (but only for existing records that were loaded)\n let isDuplicate = false;\n if (!isNew && entity) {\n isDuplicate = this.checkAndTrackRecord(entityConfig.entity, entity, filePath, arrayIndex);\n }\n \n // Save the record (always call Save, but track if it was actually dirty)\n const saveResult = await entity.Save();\n \n if (!saveResult) {\n throw new Error(`Failed to save ${entityConfig.entity} record: ${entity.LatestResult?.Message || 'Unknown error'}`);\n }\n \n // Update primaryKey for new records\n if (isNew) {\n const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);\n if (entityInfo) {\n const newPrimaryKey: Record<string, any> = {};\n for (const pk of entityInfo.PrimaryKeys) {\n newPrimaryKey[pk.Name] = entity.Get(pk.Name);\n }\n recordData.primaryKey = newPrimaryKey;\n }\n \n // Track the new record now that we have its primary key\n this.checkAndTrackRecord(entityConfig.entity, entity, filePath, arrayIndex);\n }\n \n // Only update sync metadata if the record was actually dirty (changed)\n if (isNew || isDirty) {\n recordData.sync = {\n lastModified: new Date().toISOString(),\n checksum: await this.syncEngine.calculateChecksumWithFileContent(originalFields, entityDir)\n };\n }\n \n // Restore original field values to preserve @ references\n recordData.fields = originalFields;\n \n // Write back to file only if it's a single record (not part of an array)\n if (filePath && arrayIndex === undefined && !options.dryRun) {\n await fs.writeJson(filePath, recordData, { spaces: 2 });\n }\n \n // Process related entities after parent save\n let relatedStats = { created: 0, updated: 0, unchanged: 0 };\n if (recordData.relatedEntities && !options.dryRun) {\n relatedStats = await this.processRelatedEntities(\n recordData.relatedEntities,\n entity,\n entity, // root is same as parent for top level\n entityDir,\n options,\n callbacks,\n undefined, // fileBackupManager\n 1, // indentLevel\n filePath,\n arrayIndex\n );\n }\n \n // Store related stats on the result for propagation\n // Don't count duplicates in stats\n const status: 'created' | 'updated' | 'unchanged' = isDuplicate ? 'unchanged' : (isNew ? 'created' : (isDirty ? 'updated' : 'unchanged'));\n const result = {\n status,\n relatedStats,\n isDuplicate\n };\n \n // Return enhanced result with related stats\n return result;\n }\n \n private async processRelatedEntities(\n relatedEntities: Record<string, RecordData[]>,\n parentEntity: BaseEntity,\n rootEntity: BaseEntity,\n baseDir: string,\n options: PushOptions,\n callbacks?: PushCallbacks,\n fileBackupManager?: FileBackupManager,\n indentLevel: number = 1,\n parentFilePath?: string,\n parentArrayIndex?: number\n ): Promise<{ created: number; updated: number; unchanged: number }> {\n const indent = ' '.repeat(indentLevel);\n const stats = { created: 0, updated: 0, unchanged: 0 };\n \n for (const [entityName, records] of Object.entries(relatedEntities)) {\n if (options.verbose) {\n callbacks?.onLog?.(`${indent}ā³ Processing ${records.length} related ${entityName} records`);\n }\n \n for (const relatedRecord of records) {\n try {\n // Load or create entity\n let entity = null;\n let isNew = false;\n \n if (relatedRecord.primaryKey) {\n entity = await this.syncEngine.loadEntity(entityName, relatedRecord.primaryKey);\n \n // Warn if record has primaryKey but wasn't found\n if (!entity) {\n const pkDisplay = Object.entries(relatedRecord.primaryKey)\n .map(([key, value]) => `${key}=${value}`)\n .join(', ');\n \n // Load sync config to check autoCreateMissingRecords setting\n const autoCreate = this.syncConfig?.push?.autoCreateMissingRecords ?? false;\n \n if (!autoCreate) {\n const fileRef = parentFilePath ? path.relative(configManager.getOriginalCwd(), parentFilePath) : 'unknown';\n const warning = `${indent}ā ļø Related record not found: ${entityName} with primaryKey {${pkDisplay}} at ${fileRef}`;\n this.warnings.push(warning);\n callbacks?.onWarn?.(warning);\n const warning2 = `${indent} To auto-create missing records, set push.autoCreateMissingRecords=true in .mj-sync.json`;\n this.warnings.push(warning2);\n callbacks?.onWarn?.(warning2);\n \n // Skip this record\n continue;\n } else {\n if (options.verbose) {\n callbacks?.onLog?.(`${indent} Auto-creating missing related ${entityName} record with primaryKey {${pkDisplay}}`);\n }\n }\n }\n }\n \n if (!entity) {\n entity = await this.syncEngine.createEntityObject(entityName);\n entity.NewRecord();\n isNew = true;\n \n // Handle primary keys for new related entity records\n const entityInfo = this.syncEngine.getEntityInfo(entityName);\n if (entityInfo) {\n for (const pk of entityInfo.PrimaryKeys) {\n if (!pk.AutoIncrement) {\n // Check if we have a value in primaryKey object\n if (relatedRecord.primaryKey?.[pk.Name]) {\n // User specified a primary key for new record, set it on entity directly\n // Don't add to fields as it will be in primaryKey section\n (entity as any)[pk.Name] = relatedRecord.primaryKey[pk.Name];\n if (options.verbose) {\n callbacks?.onLog?.(`${indent} Using specified primary key ${pk.Name}: ${relatedRecord.primaryKey[pk.Name]}`);\n }\n }\n // Note: BaseEntity.NewRecord() automatically generates UUIDs for uniqueidentifier primary keys\n }\n }\n }\n }\n \n // Apply fields with parent/root context\n for (const [field, value] of Object.entries(relatedRecord.fields)) {\n if (field in entity) {\n try {\n const processedValue = await this.syncEngine.processFieldValue(\n value, \n baseDir, \n parentEntity, \n rootEntity\n );\n if (options.verbose) {\n callbacks?.onLog?.(`${indent} Setting ${field}: ${this.formatFieldValue(value)} -> ${this.formatFieldValue(processedValue)}`);\n }\n (entity as any)[field] = processedValue;\n } catch (error) {\n throw new Error(`Failed to process field '${field}' in ${entityName}: ${error}`);\n }\n } else {\n const warning = `${indent} Field '${field}' does not exist on entity '${entityName}'`;\n this.warnings.push(warning);\n callbacks?.onWarn?.(warning);\n }\n }\n \n // Check for duplicate processing (but only for existing records that were loaded)\n let isDuplicate = false;\n if (!isNew && entity) {\n // Use parent file path for related entities since they're defined in the parent's file\n const relatedFilePath = parentFilePath || path.join(baseDir, 'unknown');\n isDuplicate = this.checkAndTrackRecord(entityName, entity, relatedFilePath, parentArrayIndex);\n }\n \n // Check if the record is dirty before saving\n let wasActuallyUpdated = false;\n if (!isNew && entity.Dirty) {\n // Record is dirty, get the changes\n const changes = entity.GetChangesSinceLastSave();\n const changeKeys = Object.keys(changes);\n if (changeKeys.length > 0) {\n wasActuallyUpdated = true;\n \n // Get primary key info for display\n const entityInfo = this.syncEngine.getEntityInfo(entityName);\n const primaryKeyDisplay: string[] = [];\n if (entityInfo) {\n for (const pk of entityInfo.PrimaryKeys) {\n primaryKeyDisplay.push(`${pk.Name}: ${entity.Get(pk.Name)}`);\n }\n }\n \n callbacks?.onLog?.(''); // Add newline before update output\n callbacks?.onLog?.(`${indent}š Updating related ${entityName} record:`);\n if (primaryKeyDisplay.length > 0) {\n callbacks?.onLog?.(`${indent} Primary Key: ${primaryKeyDisplay.join(', ')}`);\n }\n callbacks?.onLog?.(`${indent} Changes:`);\n for (const fieldName of changeKeys) {\n const field = entity.GetFieldByName(fieldName);\n const oldValue = field ? field.OldValue : undefined;\n const newValue = (changes as any)[fieldName];\n callbacks?.onLog?.(`${indent} ${fieldName}: ${this.formatFieldValue(oldValue)} ā ${this.formatFieldValue(newValue)}`);\n }\n }\n } else if (isNew) {\n wasActuallyUpdated = true;\n }\n \n // Save the related entity\n const saved = await entity.Save();\n if (!saved) {\n const message = entity.LatestResult?.Message;\n if (message) {\n throw new Error(`Failed to save related ${entityName}: ${message}`);\n }\n \n const errors = entity.LatestResult?.Errors?.map(err => \n typeof err === 'string' ? err : (err?.message || JSON.stringify(err))\n )?.join(', ') || 'Unknown error';\n throw new Error(`Failed to save related ${entityName}: ${errors}`);\n }\n \n // Update stats - don't count duplicates\n if (!isDuplicate) {\n if (isNew) {\n stats.created++;\n } else if (wasActuallyUpdated) {\n stats.updated++;\n } else {\n stats.unchanged++;\n }\n }\n \n if (options.verbose && wasActuallyUpdated) {\n callbacks?.onLog?.(`${indent} ā ${isNew ? 'Created' : 'Updated'} ${entityName} record`);\n } else if (options.verbose && !wasActuallyUpdated) {\n callbacks?.onLog?.(`${indent} - No changes to ${entityName} record`);\n }\n \n // Update the related record with primary key and sync metadata\n const entityInfo = this.syncEngine.getEntityInfo(entityName);\n if (entityInfo) {\n // Update primary key if new\n if (isNew) {\n relatedRecord.primaryKey = {};\n for (const pk of entityInfo.PrimaryKeys) {\n relatedRecord.primaryKey[pk.Name] = entity.Get(pk.Name);\n }\n \n // Track the new related entity now that we have its primary key\n const relatedFilePath = parentFilePath || path.join(baseDir, 'unknown');\n this.checkAndTrackRecord(entityName, entity, relatedFilePath, parentArrayIndex);\n }\n \n // Always update sync metadata\n relatedRecord.sync = {\n lastModified: new Date().toISOString(),\n checksum: this.syncEngine.calculateChecksum(relatedRecord.fields)\n };\n }\n \n // Process nested related entities if any\n if (relatedRecord.relatedEntities) {\n const nestedStats = await this.processRelatedEntities(\n relatedRecord.relatedEntities,\n entity,\n rootEntity,\n baseDir,\n options,\n callbacks,\n fileBackupManager,\n indentLevel + 1,\n parentFilePath,\n parentArrayIndex\n );\n \n // Accumulate nested stats\n stats.created += nestedStats.created;\n stats.updated += nestedStats.updated;\n stats.unchanged += nestedStats.unchanged;\n }\n } catch (error) {\n throw new Error(`Failed to process related ${entityName}: ${error}`);\n }\n }\n }\n \n return stats;\n }\n \n private isValidRecordData(data: any): data is RecordData {\n return data && \n typeof data === 'object' && \n 'fields' in data &&\n typeof data.fields === 'object';\n }\n \n private getRecordKey(recordData: RecordData, entityName: string): string {\n if (recordData.primaryKey) {\n const keys = Object.entries(recordData.primaryKey)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}:${v}`)\n .join('|');\n return `${entityName}|${keys}`;\n }\n \n // Generate a key from fields if no primary key\n const fieldKeys = Object.keys(recordData.fields).sort().join(',');\n return `${entityName}|fields:${fieldKeys}`;\n }\n \n private formatFieldValue(value: any, maxLength: number = 50): string {\n let strValue = JSON.stringify(value);\n strValue = strValue.trim();\n\n if (strValue.length > maxLength) {\n return strValue.substring(0, maxLength) + '...';\n }\n\n return strValue;\n }\n \n /**\n * Generate a unique tracking key for a record based on entity name and primary key values\n */\n private generateRecordKey(entityName: string, entity: BaseEntity): string {\n // Use the built-in CompositeKey ToURLSegment method\n const keySegment = entity.PrimaryKey.ToURLSegment();\n return `${entityName}|${keySegment}`;\n }\n \n /**\n * Check if a record has already been processed and warn if duplicate\n */\n private checkAndTrackRecord(\n entityName: string, \n entity: BaseEntity, \n filePath?: string,\n arrayIndex?: number,\n lineNumber?: number\n ): boolean {\n const recordKey = this.generateRecordKey(entityName, entity);\n \n const existing = this.processedRecords.get(recordKey);\n if (existing) {\n const primaryKeyDisplay = entity.EntityInfo?.PrimaryKeys\n .map(pk => `${pk.Name}: ${entity.Get(pk.Name)}`)\n .join(', ') || 'unknown';\n \n // Format file location with clickable link for VSCode\n // Create maps with just the line numbers we have\n const currentLineMap = lineNumber ? new Map([[arrayIndex || 0, lineNumber]]) : undefined;\n const originalLineMap = existing.lineNumber ? new Map([[existing.arrayIndex || 0, existing.lineNumber]]) : undefined;\n \n const currentLocation = this.formatFileLocation(filePath, arrayIndex, currentLineMap);\n const originalLocation = this.formatFileLocation(existing.filePath, existing.arrayIndex, originalLineMap);\n \n const warning = `ā ļø Duplicate record detected for ${entityName} (${primaryKeyDisplay})`;\n this.warnings.push(warning);\n const warning2 = ` Current location: ${currentLocation}`;\n this.warnings.push(warning2);\n const warning3 = ` Original location: ${originalLocation}`;\n this.warnings.push(warning3);\n const warning4 = ` The duplicate update will proceed, but you should review your data for unintended duplicates.`;\n this.warnings.push(warning4);\n \n return true; // is duplicate\n }\n \n // Track the record with its source location\n this.processedRecords.set(recordKey, {\n filePath: filePath || 'unknown',\n arrayIndex,\n lineNumber\n });\n return false; // not duplicate\n }\n \n /**\n * Format file location with clickable link for VSCode\n */\n private formatFileLocation(filePath?: string, arrayIndex?: number, lineNumbers?: Map<number, number>): string {\n if (!filePath || filePath === 'unknown') {\n return 'unknown';\n }\n \n // Get absolute path for better VSCode integration\n const absolutePath = path.resolve(filePath);\n \n // Try to get actual line number from our tracking\n let lineNumber = 1;\n if (arrayIndex !== undefined && lineNumbers && lineNumbers.has(arrayIndex)) {\n lineNumber = lineNumbers.get(arrayIndex)!;\n } else if (arrayIndex !== undefined) {\n // Fallback estimation if we don't have actual line numbers\n lineNumber = 2 + (arrayIndex * 15);\n }\n \n // Create clickable file path for VSCode - format: file:line\n // VSCode will make this clickable in the terminal\n return `${absolutePath}:${lineNumber}`;\n }\n \n private findEntityDirectories(baseDir: string, specificDir?: string, directoryOrder?: string[]): string[] {\n const dirs: string[] = [];\n \n if (specificDir) {\n // Process specific directory\n const fullPath = path.resolve(baseDir, specificDir);\n if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {\n // Check if this directory has an entity configuration\n const configPath = path.join(fullPath, '.mj-sync.json');\n if (fs.existsSync(configPath)) {\n try {\n const config = fs.readJsonSync(configPath);\n if (config.entity) {\n // It's an entity directory, add it\n dirs.push(fullPath);\n } else {\n // It's a container directory, search its subdirectories\n this.findEntityDirectoriesRecursive(fullPath, dirs);\n }\n } catch {\n // Invalid config, skip\n }\n }\n }\n } else {\n // Find all entity directories\n this.findEntityDirectoriesRecursive(baseDir, dirs);\n }\n \n // Apply directory ordering if specified\n if (directoryOrder && directoryOrder.length > 0 && !specificDir) {\n // Create a map of directory name to order index\n const orderMap = new Map<string, number>();\n directoryOrder.forEach((dir, index) => {\n orderMap.set(dir, index);\n });\n \n // Sort directories based on the order map\n dirs.sort((a, b) => {\n const nameA = path.basename(a);\n const nameB = path.basename(b);\n const orderA = orderMap.get(nameA) ?? Number.MAX_SAFE_INTEGER;\n const orderB = orderMap.get(nameB) ?? Number.MAX_SAFE_INTEGER;\n \n // If both have specified orders, use them\n if (orderA !== Number.MAX_SAFE_INTEGER || orderB !== Number.MAX_SAFE_INTEGER) {\n return orderA - orderB;\n }\n \n // Otherwise, maintain original order (stable sort)\n return 0;\n });\n }\n \n return dirs;\n }\n\n private findEntityDirectoriesRecursive(dir: string, dirs: string[]): void {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {\n const fullPath = path.join(dir, entry.name);\n const configPath = path.join(fullPath, '.mj-sync.json');\n \n if (fs.existsSync(configPath)) {\n try {\n const config = fs.readJsonSync(configPath);\n if (config.entity) {\n dirs.push(fullPath);\n }\n } catch {\n // Skip invalid config files\n }\n } else {\n // Recurse into subdirectories\n this.findEntityDirectoriesRecursive(fullPath, dirs);\n }\n }\n }\n }\n}"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/metadata-sync",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.65.0",
|
|
4
4
|
"description": "MemberJunction metadata synchronization CLI tool",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"metadata",
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"build": "tsc -b"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@memberjunction/core": "2.
|
|
30
|
-
"@memberjunction/core-entities": "2.
|
|
31
|
-
"@memberjunction/core-entities-server": "2.
|
|
32
|
-
"@memberjunction/global": "2.
|
|
33
|
-
"@memberjunction/sqlserver-dataprovider": "2.
|
|
34
|
-
"@memberjunction/graphql-dataprovider": "2.
|
|
29
|
+
"@memberjunction/core": "2.65.0",
|
|
30
|
+
"@memberjunction/core-entities": "2.65.0",
|
|
31
|
+
"@memberjunction/core-entities-server": "2.65.0",
|
|
32
|
+
"@memberjunction/global": "2.65.0",
|
|
33
|
+
"@memberjunction/sqlserver-dataprovider": "2.65.0",
|
|
34
|
+
"@memberjunction/graphql-dataprovider": "2.65.0",
|
|
35
35
|
"chokidar": "^3.6.0",
|
|
36
36
|
"cosmiconfig": "9.0.0",
|
|
37
37
|
"dotenv": "16.4.5",
|