@memberjunction/metadata-sync 2.54.0 → 2.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +92 -51
  2. package/dist/index.d.ts +21 -1
  3. package/dist/index.js +41 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/file-backup-manager.js +2 -2
  6. package/dist/lib/file-backup-manager.js.map +1 -1
  7. package/dist/lib/sql-logger.d.ts +44 -0
  8. package/dist/lib/sql-logger.js +140 -0
  9. package/dist/lib/sql-logger.js.map +1 -0
  10. package/dist/lib/sync-engine.js +2 -2
  11. package/dist/lib/sync-engine.js.map +1 -1
  12. package/dist/lib/transaction-manager.d.ts +36 -0
  13. package/dist/lib/transaction-manager.js +117 -0
  14. package/dist/lib/transaction-manager.js.map +1 -0
  15. package/dist/services/FileResetService.d.ts +30 -0
  16. package/dist/services/FileResetService.js +182 -0
  17. package/dist/services/FileResetService.js.map +1 -0
  18. package/dist/services/InitService.d.ts +17 -0
  19. package/dist/services/InitService.js +118 -0
  20. package/dist/services/InitService.js.map +1 -0
  21. package/dist/services/PullService.d.ts +45 -0
  22. package/dist/services/PullService.js +564 -0
  23. package/dist/services/PullService.js.map +1 -0
  24. package/dist/services/PushService.d.ts +45 -0
  25. package/dist/services/PushService.js +394 -0
  26. package/dist/services/PushService.js.map +1 -0
  27. package/dist/services/StatusService.d.ts +32 -0
  28. package/dist/services/StatusService.js +138 -0
  29. package/dist/services/StatusService.js.map +1 -0
  30. package/dist/services/WatchService.d.ts +32 -0
  31. package/dist/services/WatchService.js +242 -0
  32. package/dist/services/WatchService.js.map +1 -0
  33. package/dist/services/index.d.ts +16 -0
  34. package/dist/services/index.js +28 -0
  35. package/dist/services/index.js.map +1 -0
  36. package/package.json +14 -45
  37. package/bin/debug.js +0 -7
  38. package/bin/run +0 -17
  39. package/bin/run.js +0 -6
  40. package/dist/commands/file-reset/index.d.ts +0 -15
  41. package/dist/commands/file-reset/index.js +0 -221
  42. package/dist/commands/file-reset/index.js.map +0 -1
  43. package/dist/commands/init/index.d.ts +0 -7
  44. package/dist/commands/init/index.js +0 -155
  45. package/dist/commands/init/index.js.map +0 -1
  46. package/dist/commands/pull/index.d.ts +0 -246
  47. package/dist/commands/pull/index.js +0 -1448
  48. package/dist/commands/pull/index.js.map +0 -1
  49. package/dist/commands/push/index.d.ts +0 -41
  50. package/dist/commands/push/index.js +0 -1131
  51. package/dist/commands/push/index.js.map +0 -1
  52. package/dist/commands/status/index.d.ts +0 -10
  53. package/dist/commands/status/index.js +0 -199
  54. package/dist/commands/status/index.js.map +0 -1
  55. package/dist/commands/validate/index.d.ts +0 -15
  56. package/dist/commands/validate/index.js +0 -149
  57. package/dist/commands/validate/index.js.map +0 -1
  58. package/dist/commands/watch/index.d.ts +0 -15
  59. package/dist/commands/watch/index.js +0 -300
  60. package/dist/commands/watch/index.js.map +0 -1
  61. package/dist/hooks/init.d.ts +0 -3
  62. package/dist/hooks/init.js +0 -59
  63. package/dist/hooks/init.js.map +0 -1
  64. package/oclif.manifest.json +0 -376
package/README.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # MemberJunction Metadata Sync
2
2
 
3
- A CLI tool for synchronizing MemberJunction database metadata with local file system representations. This tool enables developers and non-technical users to manage MJ metadata using their preferred editors and version control systems while maintaining the database as the source of truth.
3
+ A library for synchronizing MemberJunction database metadata with local file system representations. This library is integrated into the MemberJunction CLI (`mj`) and is accessed through `mj sync` commands. It enables developers and non-technical users to manage MJ metadata using their preferred editors and version control systems while maintaining the database as the source of truth.
4
+
5
+ ## Installation
6
+
7
+ MetadataSync is included with the MemberJunction CLI. Install the CLI globally:
8
+
9
+ ```bash
10
+ npm install -g @memberjunction/cli
11
+ ```
12
+
13
+ Then use the sync commands:
14
+ ```bash
15
+ mj sync --help
16
+ ```
4
17
 
5
18
  ## Purpose
6
19
 
@@ -401,10 +414,10 @@ echo '{
401
414
  #### Step 4: Test and Validate
402
415
  ```bash
403
416
  # Dry run to check for errors
404
- mj-sync push --dir="templates" --dry-run
417
+ mj sync push --dir="templates" --dry-run
405
418
 
406
419
  # If successful, do actual push
407
- mj-sync push --dir="templates"
420
+ mj sync push --dir="templates"
408
421
  ```
409
422
 
410
423
  ### AI/LLM Guidelines
@@ -896,60 +909,65 @@ Templates can reference other templates:
896
909
 
897
910
  ## CLI Commands
898
911
 
912
+ All MetadataSync functionality is accessed through the MemberJunction CLI (`mj`) under the `sync` namespace. The commands previously available through `mj-sync` are now integrated as `mj sync` commands:
913
+
899
914
  ```bash
900
915
  # Validate all metadata files
901
- mj-sync validate
916
+ mj sync validate
902
917
 
903
918
  # Validate a specific directory
904
- mj-sync validate --dir="./metadata"
919
+ mj sync validate --dir="./metadata"
905
920
 
906
921
  # Validate with detailed output
907
- mj-sync validate --verbose
922
+ mj sync validate --verbose
908
923
 
909
924
  # Validate with JSON output for CI/CD
910
- mj-sync validate --format=json
925
+ mj sync validate --format=json
911
926
 
912
927
  # Save validation report to markdown file
913
- mj-sync validate --save-report
928
+ mj sync validate --save-report
914
929
 
915
930
  # Initialize a directory for metadata sync
916
- mj-sync init
931
+ mj sync init
917
932
 
918
933
  # Pull all AI Prompts from database to ai-prompts directory
919
- mj-sync pull --entity="AI Prompts"
934
+ mj sync pull --entity="AI Prompts"
920
935
 
921
936
  # Pull specific records by filter
922
- mj-sync pull --entity="AI Prompts" --filter="CategoryID='customer-service-id'"
937
+ mj sync pull --entity="AI Prompts" --filter="CategoryID='customer-service-id'"
923
938
 
924
939
  # Pull multiple records into a single file (NEW)
925
- mj-sync pull --entity="AI Prompts" --multi-file="all-prompts"
926
- mj-sync pull --entity="AI Prompts" --filter="Status='Active'" --multi-file="active-prompts.json"
940
+ mj sync pull --entity="AI Prompts" --multi-file="all-prompts"
941
+ mj sync pull --entity="AI Prompts" --filter="Status='Active'" --multi-file="active-prompts.json"
927
942
 
928
943
  # Push all changes from current directory and subdirectories
929
- mj-sync push
944
+ mj sync push
930
945
 
931
946
  # Push only specific entity directory
932
- mj-sync push --dir="ai-prompts"
947
+ mj sync push --dir="ai-prompts"
933
948
 
934
949
  # Push with verbose output (NEW)
935
- mj-sync push -v
936
- mj-sync push --verbose
950
+ mj sync push -v
951
+ mj sync push --verbose
937
952
 
938
953
  # Dry run to see what would change
939
- mj-sync push --dry-run
954
+ mj sync push --dry-run
940
955
 
941
956
  # Show status of local vs database
942
- mj-sync status
957
+ mj sync status
943
958
 
944
959
  # Watch for changes and auto-push
945
- mj-sync watch
960
+ mj sync watch
946
961
 
947
962
  # CI/CD mode (push with no prompts, fails on validation errors)
948
- mj-sync push --ci
963
+ mj sync push --ci
949
964
 
950
965
  # Push/Pull without validation
951
- mj-sync push --no-validate
952
- mj-sync pull --entity="AI Prompts" --no-validate
966
+ mj sync push --no-validate
967
+ mj sync pull --entity="AI Prompts" --no-validate
968
+
969
+ # Reset file checksums after manual edits
970
+ mj sync file-reset
953
971
  ```
954
972
 
955
973
  ## Configuration
@@ -1103,7 +1121,7 @@ Migration files include:
1103
1121
 
1104
1122
  2. **Run push command** as normal:
1105
1123
  ```bash
1106
- mj-sync push
1124
+ mj sync push
1107
1125
  ```
1108
1126
 
1109
1127
  3. **Review generated SQL** in the output directory:
@@ -1637,12 +1655,13 @@ Processing AI Prompts in demo/ai-prompts
1637
1655
  ## Use Cases
1638
1656
 
1639
1657
  ### Developer Workflow
1640
- 1. `mj-sync pull --entity="AI Prompts"` to get latest prompts with their models
1641
- 2. Edit prompts and adjust model configurations in VS Code
1642
- 3. Test locally with `mj-sync push --dry-run`
1643
- 4. Commit changes to Git
1644
- 5. PR review with diff visualization
1645
- 6. CI/CD runs `mj-sync push --ci` on merge
1658
+ 1. Install the MJ CLI: `npm install -g @memberjunction/cli`
1659
+ 2. `mj sync pull --entity="AI Prompts"` to get latest prompts with their models
1660
+ 3. Edit prompts and adjust model configurations in VS Code
1661
+ 4. Test locally with `mj sync push --dry-run`
1662
+ 5. Commit changes to Git
1663
+ 6. PR review with diff visualization
1664
+ 7. CI/CD runs `mj sync push --ci` on merge
1646
1665
 
1647
1666
  ### Content Team Workflow
1648
1667
  1. Pull prompts to local directory
@@ -1655,8 +1674,8 @@ Processing AI Prompts in demo/ai-prompts
1655
1674
  ```yaml
1656
1675
  - name: Push Metadata to Production
1657
1676
  run: |
1658
- npm install -g @memberjunction/metadata-sync
1659
- mj-sync push --ci --entity="AI Prompts"
1677
+ npm install -g @memberjunction/cli
1678
+ mj sync push --ci --entity="AI Prompts"
1660
1679
  ```
1661
1680
 
1662
1681
  ## Benefits
@@ -1691,31 +1710,31 @@ The MetadataSync tool includes a comprehensive validation system that checks you
1691
1710
  By default, validation runs automatically before push and pull operations:
1692
1711
  ```bash
1693
1712
  # These commands validate first, then proceed if valid
1694
- mj-sync push
1695
- mj-sync pull --entity="AI Prompts"
1713
+ mj sync push
1714
+ mj sync pull --entity="AI Prompts"
1696
1715
  ```
1697
1716
 
1698
1717
  #### Manual Validation
1699
1718
  Run validation without performing any sync operations:
1700
1719
  ```bash
1701
1720
  # Validate current directory
1702
- mj-sync validate
1721
+ mj sync validate
1703
1722
 
1704
1723
  # Validate specific directory
1705
- mj-sync validate --dir="./metadata"
1724
+ mj sync validate --dir="./metadata"
1706
1725
 
1707
1726
  # Verbose output shows all files checked
1708
- mj-sync validate --verbose
1727
+ mj sync validate --verbose
1709
1728
  ```
1710
1729
 
1711
1730
  #### CI/CD Integration
1712
1731
  Get JSON output for automated pipelines:
1713
1732
  ```bash
1714
1733
  # JSON output for parsing
1715
- mj-sync validate --format=json
1734
+ mj sync validate --format=json
1716
1735
 
1717
1736
  # In CI mode, validation failures cause immediate exit
1718
- mj-sync push --ci
1737
+ mj sync push --ci
1719
1738
  ```
1720
1739
 
1721
1740
  #### Validation During Push
@@ -1730,8 +1749,8 @@ mj-sync push --ci
1730
1749
  For emergency fixes or when you know validation will fail:
1731
1750
  ```bash
1732
1751
  # Skip validation checks (USE WITH CAUTION!)
1733
- mj-sync push --no-validate
1734
- mj-sync pull --entity="AI Prompts" --no-validate
1752
+ mj sync push --no-validate
1753
+ mj sync pull --entity="AI Prompts" --no-validate
1735
1754
  ```
1736
1755
 
1737
1756
  ⚠️ **Warning:** Using `--no-validate` may push invalid metadata to your database, potentially breaking your application. Only use this flag when absolutely necessary.
@@ -1803,7 +1822,7 @@ Errors
1803
1822
  Entity: Templates
1804
1823
  Field: Status
1805
1824
  File: ./metadata/templates/.my-template.json
1806
- → Suggestion: Check spelling of 'Status'. Run 'mj-sync list-entities' to see available entities.
1825
+ → Suggestion: Check spelling of 'Status'. Run 'mj sync list-entities' to see available entities.
1807
1826
 
1808
1827
  2. File not found: ./shared/footer.html
1809
1828
  Entity: Templates
@@ -1837,7 +1856,7 @@ Errors
1837
1856
  "field": "Status",
1838
1857
  "file": "./metadata/templates/.my-template.json",
1839
1858
  "message": "Field \"Status\" does not exist on entity \"Templates\"",
1840
- "suggestion": "Check spelling of 'Status'. Run 'mj-sync list-entities' to see available entities."
1859
+ "suggestion": "Check spelling of 'Status'. Run 'mj sync list-entities' to see available entities."
1841
1860
  }
1842
1861
  ],
1843
1862
  "warnings": [...]
@@ -1931,9 +1950,9 @@ Control validation behavior in your workflow:
1931
1950
 
1932
1951
  ### Best Practices
1933
1952
 
1934
- 1. **Run validation during development**: `mj-sync validate` frequently
1953
+ 1. **Run validation during development**: `mj sync validate` frequently
1935
1954
  2. **Fix errors before warnings**: Errors block operations, warnings don't
1936
- 3. **Use verbose mode** to understand issues: `mj-sync validate -v`
1955
+ 3. **Use verbose mode** to understand issues: `mj sync validate -v`
1937
1956
  4. **Include in CI/CD**: Parse JSON output for automated checks
1938
1957
  5. **Don't skip validation** unless absolutely necessary
1939
1958
 
@@ -1945,7 +1964,7 @@ If validation fails:
1945
1964
 
1946
1965
  1. **Read the error message carefully** - It includes specific details
1947
1966
  2. **Check the suggestion** - Most errors include how to fix them
1948
- 3. **Use verbose mode** for more context: `mj-sync validate -v`
1967
+ 3. **Use verbose mode** for more context: `mj sync validate -v`
1949
1968
  4. **Verify entity definitions** in generated entity files
1950
1969
  5. **Check file paths** are relative to the metadata directory
1951
1970
 
@@ -1953,9 +1972,9 @@ If validation fails:
1953
1972
 
1954
1973
  For large metadata sets:
1955
1974
 
1956
- 1. **Disable best practice checks**: `mj-sync validate --no-best-practices`
1957
- 2. **Validate specific directories**: `mj-sync validate --dir="./prompts"`
1958
- 3. **Reduce nesting depth warning**: `mj-sync validate --max-depth=20`
1975
+ 1. **Disable best practice checks**: `mj sync validate --no-best-practices`
1976
+ 2. **Validate specific directories**: `mj sync validate --dir="./prompts"`
1977
+ 3. **Reduce nesting depth warning**: `mj sync validate --max-depth=20`
1959
1978
 
1960
1979
  ## Programmatic Usage
1961
1980
 
@@ -2074,8 +2093,8 @@ export async function validateBeforeDeploy(metadataPath: string): Promise<boolea
2074
2093
  # Example GitHub Actions workflow
2075
2094
  - name: Validate Metadata
2076
2095
  run: |
2077
- npm install @memberjunction/metadata-sync
2078
- npx mj-sync validate --dir=./metadata --format=json > validation-results.json
2096
+ npm install @memberjunction/cli
2097
+ npx mj sync validate --dir=./metadata --format=json > validation-results.json
2079
2098
 
2080
2099
  - name: Check Validation Results
2081
2100
  run: |
@@ -2096,3 +2115,25 @@ export async function validateBeforeDeploy(metadataPath: string): Promise<boolea
2096
2115
  - Team collaboration features
2097
2116
  - Bidirectional sync for related entities
2098
2117
  - Custom transformation pipelines
2118
+
2119
+ ## Migration from Standalone MetadataSync
2120
+
2121
+ If you were previously using the standalone `mj-sync` command:
2122
+
2123
+ 1. **Update your installation**: Install the MJ CLI instead of standalone MetadataSync
2124
+ ```bash
2125
+ npm install -g @memberjunction/cli
2126
+ ```
2127
+
2128
+ 2. **Update your scripts**: Replace `mj-sync` with `mj sync` in all scripts and documentation
2129
+ ```bash
2130
+ # Old command (standalone package)
2131
+ mj-sync push --dir="metadata"
2132
+
2133
+ # New command
2134
+ mj sync push --dir="metadata"
2135
+ ```
2136
+
2137
+ 3. **Configuration unchanged**: All `.mj-sync.json` configuration files work exactly the same
2138
+
2139
+ 4. **Same functionality**: The underlying MetadataSync library is identical, just accessed through the unified CLI
package/dist/index.d.ts CHANGED
@@ -1,4 +1,24 @@
1
- export { run } from '@oclif/core';
2
1
  export { FileBackupManager } from './lib/file-backup-manager';
3
2
  export { SyncEngine } from './lib/sync-engine';
4
3
  export type { RecordData } from './lib/sync-engine';
4
+ export { ConfigManager, configManager } from './lib/config-manager';
5
+ export { getSyncEngine, resetSyncEngine } from './lib/singleton-manager';
6
+ export { SQLLogger } from './lib/sql-logger';
7
+ export { TransactionManager } from './lib/transaction-manager';
8
+ export { InitService } from './services/InitService';
9
+ export type { InitOptions, InitCallbacks } from './services/InitService';
10
+ export { PullService } from './services/PullService';
11
+ export type { PullOptions, PullCallbacks, PullResult } from './services/PullService';
12
+ export { PushService } from './services/PushService';
13
+ export type { PushOptions, PushCallbacks, PushResult } from './services/PushService';
14
+ export { StatusService } from './services/StatusService';
15
+ export type { StatusOptions, StatusCallbacks, StatusResult } from './services/StatusService';
16
+ export { FileResetService } from './services/FileResetService';
17
+ export type { FileResetOptions, FileResetCallbacks, FileResetResult } from './services/FileResetService';
18
+ export { WatchService } from './services/WatchService';
19
+ export type { WatchOptions, WatchCallbacks, WatchResult } from './services/WatchService';
20
+ export { ValidationService } from './services/ValidationService';
21
+ export { FormattingService } from './services/FormattingService';
22
+ export { loadMJConfig, loadSyncConfig, loadEntityConfig, loadFolderConfig, type EntityConfig, type FolderConfig, type RelatedEntityConfig } from './config';
23
+ export { initializeProvider, getSystemUser, findEntityDirectories, getDataProvider } from './lib/provider-utils';
24
+ export type { ValidationResult, ValidationError, ValidationWarning, EntityDependency, FileValidationResult, ValidationOptions, ReferenceType, ParsedReference } from './types/validation';
package/dist/index.js CHANGED
@@ -1,10 +1,48 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SyncEngine = exports.FileBackupManager = exports.run = void 0;
4
- var core_1 = require("@oclif/core");
5
- Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
3
+ exports.getDataProvider = exports.findEntityDirectories = exports.getSystemUser = exports.initializeProvider = exports.loadFolderConfig = exports.loadEntityConfig = exports.loadSyncConfig = exports.loadMJConfig = exports.FormattingService = exports.ValidationService = exports.WatchService = exports.FileResetService = exports.StatusService = exports.PushService = exports.PullService = exports.InitService = exports.TransactionManager = exports.SQLLogger = exports.resetSyncEngine = exports.getSyncEngine = exports.configManager = exports.ConfigManager = exports.SyncEngine = exports.FileBackupManager = void 0;
4
+ // Core library exports
6
5
  var file_backup_manager_1 = require("./lib/file-backup-manager");
7
6
  Object.defineProperty(exports, "FileBackupManager", { enumerable: true, get: function () { return file_backup_manager_1.FileBackupManager; } });
8
7
  var sync_engine_1 = require("./lib/sync-engine");
9
8
  Object.defineProperty(exports, "SyncEngine", { enumerable: true, get: function () { return sync_engine_1.SyncEngine; } });
9
+ var config_manager_1 = require("./lib/config-manager");
10
+ Object.defineProperty(exports, "ConfigManager", { enumerable: true, get: function () { return config_manager_1.ConfigManager; } });
11
+ Object.defineProperty(exports, "configManager", { enumerable: true, get: function () { return config_manager_1.configManager; } });
12
+ var singleton_manager_1 = require("./lib/singleton-manager");
13
+ Object.defineProperty(exports, "getSyncEngine", { enumerable: true, get: function () { return singleton_manager_1.getSyncEngine; } });
14
+ Object.defineProperty(exports, "resetSyncEngine", { enumerable: true, get: function () { return singleton_manager_1.resetSyncEngine; } });
15
+ var sql_logger_1 = require("./lib/sql-logger");
16
+ Object.defineProperty(exports, "SQLLogger", { enumerable: true, get: function () { return sql_logger_1.SQLLogger; } });
17
+ var transaction_manager_1 = require("./lib/transaction-manager");
18
+ Object.defineProperty(exports, "TransactionManager", { enumerable: true, get: function () { return transaction_manager_1.TransactionManager; } });
19
+ // Service exports
20
+ var InitService_1 = require("./services/InitService");
21
+ Object.defineProperty(exports, "InitService", { enumerable: true, get: function () { return InitService_1.InitService; } });
22
+ var PullService_1 = require("./services/PullService");
23
+ Object.defineProperty(exports, "PullService", { enumerable: true, get: function () { return PullService_1.PullService; } });
24
+ var PushService_1 = require("./services/PushService");
25
+ Object.defineProperty(exports, "PushService", { enumerable: true, get: function () { return PushService_1.PushService; } });
26
+ var StatusService_1 = require("./services/StatusService");
27
+ Object.defineProperty(exports, "StatusService", { enumerable: true, get: function () { return StatusService_1.StatusService; } });
28
+ var FileResetService_1 = require("./services/FileResetService");
29
+ Object.defineProperty(exports, "FileResetService", { enumerable: true, get: function () { return FileResetService_1.FileResetService; } });
30
+ var WatchService_1 = require("./services/WatchService");
31
+ Object.defineProperty(exports, "WatchService", { enumerable: true, get: function () { return WatchService_1.WatchService; } });
32
+ var ValidationService_1 = require("./services/ValidationService");
33
+ Object.defineProperty(exports, "ValidationService", { enumerable: true, get: function () { return ValidationService_1.ValidationService; } });
34
+ var FormattingService_1 = require("./services/FormattingService");
35
+ Object.defineProperty(exports, "FormattingService", { enumerable: true, get: function () { return FormattingService_1.FormattingService; } });
36
+ // Configuration types
37
+ var config_1 = require("./config");
38
+ Object.defineProperty(exports, "loadMJConfig", { enumerable: true, get: function () { return config_1.loadMJConfig; } });
39
+ Object.defineProperty(exports, "loadSyncConfig", { enumerable: true, get: function () { return config_1.loadSyncConfig; } });
40
+ Object.defineProperty(exports, "loadEntityConfig", { enumerable: true, get: function () { return config_1.loadEntityConfig; } });
41
+ Object.defineProperty(exports, "loadFolderConfig", { enumerable: true, get: function () { return config_1.loadFolderConfig; } });
42
+ // Provider utilities
43
+ var provider_utils_1 = require("./lib/provider-utils");
44
+ Object.defineProperty(exports, "initializeProvider", { enumerable: true, get: function () { return provider_utils_1.initializeProvider; } });
45
+ Object.defineProperty(exports, "getSystemUser", { enumerable: true, get: function () { return provider_utils_1.getSystemUser; } });
46
+ Object.defineProperty(exports, "findEntityDirectories", { enumerable: true, get: function () { return provider_utils_1.findEntityDirectories; } });
47
+ Object.defineProperty(exports, "getDataProvider", { enumerable: true, get: function () { return provider_utils_1.getDataProvider; } });
10
48
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oCAAkC;AAAzB,2FAAA,GAAG,OAAA;AACZ,iEAA8D;AAArD,wHAAA,iBAAiB,OAAA;AAC1B,iDAA+C;AAAtC,yGAAA,UAAU,OAAA","sourcesContent":["export { run } from '@oclif/core';\nexport { FileBackupManager } from './lib/file-backup-manager';\nexport { SyncEngine } from './lib/sync-engine';\nexport type { RecordData } from './lib/sync-engine';"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uBAAuB;AACvB,iEAA8D;AAArD,wHAAA,iBAAiB,OAAA;AAC1B,iDAA+C;AAAtC,yGAAA,UAAU,OAAA;AAEnB,uDAAoE;AAA3D,+GAAA,aAAa,OAAA;AAAE,+GAAA,aAAa,OAAA;AACrC,6DAAyE;AAAhE,kHAAA,aAAa,OAAA;AAAE,oHAAA,eAAe,OAAA;AACvC,+CAA6C;AAApC,uGAAA,SAAS,OAAA;AAClB,iEAA+D;AAAtD,yHAAA,kBAAkB,OAAA;AAE3B,kBAAkB;AAClB,sDAAqD;AAA5C,0GAAA,WAAW,OAAA;AAGpB,sDAAqD;AAA5C,0GAAA,WAAW,OAAA;AAGpB,sDAAqD;AAA5C,0GAAA,WAAW,OAAA;AAGpB,0DAAyD;AAAhD,8GAAA,aAAa,OAAA;AAGtB,gEAA+D;AAAtD,oHAAA,gBAAgB,OAAA;AAGzB,wDAAuD;AAA9C,4GAAA,YAAY,OAAA;AAGrB,kEAAiE;AAAxD,sHAAA,iBAAiB,OAAA;AAC1B,kEAAiE;AAAxD,sHAAA,iBAAiB,OAAA;AAE1B,sBAAsB;AACtB,mCAQkB;AAPhB,sGAAA,YAAY,OAAA;AACZ,wGAAA,cAAc,OAAA;AACd,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAMlB,qBAAqB;AACrB,uDAK8B;AAJ5B,oHAAA,kBAAkB,OAAA;AAClB,+GAAA,aAAa,OAAA;AACb,uHAAA,qBAAqB,OAAA;AACrB,iHAAA,eAAe,OAAA","sourcesContent":["// Core library exports\nexport { FileBackupManager } from './lib/file-backup-manager';\nexport { SyncEngine } from './lib/sync-engine';\nexport type { RecordData } from './lib/sync-engine';\nexport { ConfigManager, configManager } from './lib/config-manager';\nexport { getSyncEngine, resetSyncEngine } from './lib/singleton-manager';\nexport { SQLLogger } from './lib/sql-logger';\nexport { TransactionManager } from './lib/transaction-manager';\n\n// Service exports\nexport { InitService } from './services/InitService';\nexport type { InitOptions, InitCallbacks } from './services/InitService';\n\nexport { PullService } from './services/PullService';\nexport type { PullOptions, PullCallbacks, PullResult } from './services/PullService';\n\nexport { PushService } from './services/PushService';\nexport type { PushOptions, PushCallbacks, PushResult } from './services/PushService';\n\nexport { StatusService } from './services/StatusService';\nexport type { StatusOptions, StatusCallbacks, StatusResult } from './services/StatusService';\n\nexport { FileResetService } from './services/FileResetService';\nexport type { FileResetOptions, FileResetCallbacks, FileResetResult } from './services/FileResetService';\n\nexport { WatchService } from './services/WatchService';\nexport type { WatchOptions, WatchCallbacks, WatchResult } from './services/WatchService';\n\nexport { ValidationService } from './services/ValidationService';\nexport { FormattingService } from './services/FormattingService';\n\n// Configuration types\nexport {\n loadMJConfig,\n loadSyncConfig,\n loadEntityConfig,\n loadFolderConfig,\n type EntityConfig,\n type FolderConfig,\n type RelatedEntityConfig\n} from './config';\n\n// Provider utilities\nexport {\n initializeProvider,\n getSystemUser,\n findEntityDirectories,\n getDataProvider\n} from './lib/provider-utils';\n\n// Validation types\nexport type {\n ValidationResult,\n ValidationError,\n ValidationWarning,\n EntityDependency,\n FileValidationResult,\n ValidationOptions,\n ReferenceType,\n ParsedReference\n} from './types/validation';"]}
@@ -15,7 +15,7 @@ exports.FileBackupManager = void 0;
15
15
  const fs_extra_1 = __importDefault(require("fs-extra"));
16
16
  const path_1 = __importDefault(require("path"));
17
17
  const os_1 = __importDefault(require("os"));
18
- const global_1 = require("@memberjunction/global");
18
+ const uuid_1 = require("uuid");
19
19
  /**
20
20
  * Manages file backups and rollback operations for MetadataSync
21
21
  *
@@ -61,7 +61,7 @@ class FileBackupManager {
61
61
  }
62
62
  // Create a unique temporary directory for this session
63
63
  const tempRoot = os_1.default.tmpdir();
64
- const sessionId = (0, global_1.uuidv4)();
64
+ const sessionId = (0, uuid_1.v4)();
65
65
  this.backupDir = path_1.default.join(tempRoot, 'mj-metadata-sync', sessionId);
66
66
  await fs_extra_1.default.ensureDir(this.backupDir);
67
67
  this.initialized = true;
@@ -1 +1 @@
1
- {"version":3,"file":"file-backup-manager.js","sourceRoot":"","sources":["../../src/lib/file-backup-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview File backup and rollback manager for MetadataSync operations\n * @module file-backup-manager\n * \n * This module provides functionality to create backups of files before modification\n * and allows rolling back all changes if any operation fails. It ensures atomic\n * file operations by maintaining a temporary backup directory during push operations.\n */\n\nimport fs from 'fs-extra';\nimport path from 'path';\nimport os from 'os';\nimport { uuidv4 } from '@memberjunction/global';\n\n/**\n * Represents a backed-up file with its original and backup paths\n */\ninterface BackupEntry {\n /** Original file path that was backed up */\n originalPath: string;\n /** Path to the backup copy of the file */\n backupPath: string;\n /** Whether the file existed before modification */\n existed: boolean;\n}\n\n/**\n * Manages file backups and rollback operations for MetadataSync\n * \n * Creates temporary backups of all modified files during a push operation,\n * allowing atomic rollback of all file changes if any error occurs.\n * \n * @class FileBackupManager\n * @example\n * ```typescript\n * const backupManager = new FileBackupManager();\n * await backupManager.initialize();\n * \n * try {\n * await backupManager.backupFile('/path/to/file.json');\n * // Modify the file\n * await fs.writeJson('/path/to/file.json', newData);\n * \n * // If all operations succeed\n * await backupManager.cleanup();\n * } catch (error) {\n * // Rollback all file changes\n * await backupManager.rollback();\n * throw error;\n * }\n * ```\n */\nexport class FileBackupManager {\n private backupDir: string = '';\n private backups: BackupEntry[] = [];\n private initialized: boolean = false;\n \n /**\n * Initialize the backup manager by creating a temporary directory\n * \n * Creates a unique temporary directory for storing file backups during\n * the current operation. Must be called before any backup operations.\n * \n * @returns Promise that resolves when initialization is complete\n * @throws Error if temporary directory creation fails\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n throw new Error('FileBackupManager already initialized');\n }\n \n // Create a unique temporary directory for this session\n const tempRoot = os.tmpdir();\n const sessionId = uuidv4();\n this.backupDir = path.join(tempRoot, 'mj-metadata-sync', sessionId);\n \n await fs.ensureDir(this.backupDir);\n this.initialized = true;\n }\n \n /**\n * Create a backup of a file before modification\n * \n * Copies the current state of a file to the backup directory. If the file\n * doesn't exist, records that fact for proper rollback handling.\n * \n * @param filePath - Absolute path to the file to backup\n * @returns Promise that resolves when backup is complete\n * @throws Error if backup operation fails\n */\n async backupFile(filePath: string): Promise<void> {\n if (!this.initialized) {\n throw new Error('FileBackupManager not initialized. Call initialize() first.');\n }\n \n // Check if we already have a backup for this file\n const existingBackup = this.backups.find(b => b.originalPath === filePath);\n if (existingBackup) {\n // Already backed up, don't backup again\n return;\n }\n \n const exists = await fs.pathExists(filePath);\n const backupFileName = `${path.basename(filePath)}_${Date.now()}_${this.backups.length}`;\n const backupPath = path.join(this.backupDir, backupFileName);\n \n const backupEntry: BackupEntry = {\n originalPath: filePath,\n backupPath: backupPath,\n existed: exists\n };\n \n if (exists) {\n // Copy the file to backup location\n await fs.copy(filePath, backupPath);\n }\n \n this.backups.push(backupEntry);\n }\n \n /**\n * Rollback all file changes by restoring from backups\n * \n * Restores all backed-up files to their original state. Files that didn't\n * exist before the operation are deleted. This operation is atomic - either\n * all files are restored or none are.\n * \n * @returns Promise that resolves when rollback is complete\n * @throws Error if any rollback operation fails\n */\n async rollback(): Promise<void> {\n if (!this.initialized) {\n return; // Nothing to rollback\n }\n \n const errors: string[] = [];\n \n // Process backups in reverse order (last modified first)\n for (const backup of this.backups.reverse()) {\n try {\n if (backup.existed) {\n // Restore the original file\n await fs.copy(backup.backupPath, backup.originalPath, { overwrite: true });\n } else {\n // File didn't exist before, remove it\n if (await fs.pathExists(backup.originalPath)) {\n await fs.remove(backup.originalPath);\n }\n }\n } catch (error) {\n const errorMsg = `Failed to rollback ${backup.originalPath}: ${error instanceof Error ? error.message : String(error)}`;\n errors.push(errorMsg);\n console.error(errorMsg);\n }\n }\n \n // Clean up backup directory\n try {\n await this.cleanup();\n } catch (error) {\n console.error('Failed to cleanup backup directory during rollback:', error);\n }\n \n if (errors.length > 0) {\n throw new Error(`Rollback completed with errors:\\n${errors.join('\\n')}`);\n }\n }\n \n /**\n * Clean up backup directory after successful operation\n * \n * Removes all temporary backup files and the backup directory. Should be\n * called after all operations complete successfully.\n * \n * @returns Promise that resolves when cleanup is complete\n */\n async cleanup(): Promise<void> {\n if (!this.initialized || !this.backupDir) {\n return;\n }\n \n try {\n await fs.remove(this.backupDir);\n } catch (error) {\n // Log but don't throw - cleanup errors shouldn't fail the operation\n console.warn(`Failed to cleanup backup directory ${this.backupDir}:`, error);\n }\n \n this.backups = [];\n this.initialized = false;\n this.backupDir = '';\n }\n \n /**\n * Get statistics about current backup session\n * \n * @returns Object containing backup statistics\n */\n getStats(): { totalBackups: number; backupDir: string; initialized: boolean } {\n return {\n totalBackups: this.backups.length,\n backupDir: this.backupDir,\n initialized: this.initialized\n };\n }\n}"]}
1
+ {"version":3,"file":"file-backup-manager.js","sourceRoot":"","sources":["../../src/lib/file-backup-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview File backup and rollback manager for MetadataSync operations\n * @module file-backup-manager\n * \n * This module provides functionality to create backups of files before modification\n * and allows rolling back all changes if any operation fails. It ensures atomic\n * file operations by maintaining a temporary backup directory during push operations.\n */\n\nimport fs from 'fs-extra';\nimport path from 'path';\nimport os from 'os';\nimport { v4 as uuidv4 } from 'uuid';\n\n/**\n * Represents a backed-up file with its original and backup paths\n */\ninterface BackupEntry {\n /** Original file path that was backed up */\n originalPath: string;\n /** Path to the backup copy of the file */\n backupPath: string;\n /** Whether the file existed before modification */\n existed: boolean;\n}\n\n/**\n * Manages file backups and rollback operations for MetadataSync\n * \n * Creates temporary backups of all modified files during a push operation,\n * allowing atomic rollback of all file changes if any error occurs.\n * \n * @class FileBackupManager\n * @example\n * ```typescript\n * const backupManager = new FileBackupManager();\n * await backupManager.initialize();\n * \n * try {\n * await backupManager.backupFile('/path/to/file.json');\n * // Modify the file\n * await fs.writeJson('/path/to/file.json', newData);\n * \n * // If all operations succeed\n * await backupManager.cleanup();\n * } catch (error) {\n * // Rollback all file changes\n * await backupManager.rollback();\n * throw error;\n * }\n * ```\n */\nexport class FileBackupManager {\n private backupDir: string = '';\n private backups: BackupEntry[] = [];\n private initialized: boolean = false;\n \n /**\n * Initialize the backup manager by creating a temporary directory\n * \n * Creates a unique temporary directory for storing file backups during\n * the current operation. Must be called before any backup operations.\n * \n * @returns Promise that resolves when initialization is complete\n * @throws Error if temporary directory creation fails\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n throw new Error('FileBackupManager already initialized');\n }\n \n // Create a unique temporary directory for this session\n const tempRoot = os.tmpdir();\n const sessionId = uuidv4();\n this.backupDir = path.join(tempRoot, 'mj-metadata-sync', sessionId);\n \n await fs.ensureDir(this.backupDir);\n this.initialized = true;\n }\n \n /**\n * Create a backup of a file before modification\n * \n * Copies the current state of a file to the backup directory. If the file\n * doesn't exist, records that fact for proper rollback handling.\n * \n * @param filePath - Absolute path to the file to backup\n * @returns Promise that resolves when backup is complete\n * @throws Error if backup operation fails\n */\n async backupFile(filePath: string): Promise<void> {\n if (!this.initialized) {\n throw new Error('FileBackupManager not initialized. Call initialize() first.');\n }\n \n // Check if we already have a backup for this file\n const existingBackup = this.backups.find(b => b.originalPath === filePath);\n if (existingBackup) {\n // Already backed up, don't backup again\n return;\n }\n \n const exists = await fs.pathExists(filePath);\n const backupFileName = `${path.basename(filePath)}_${Date.now()}_${this.backups.length}`;\n const backupPath = path.join(this.backupDir, backupFileName);\n \n const backupEntry: BackupEntry = {\n originalPath: filePath,\n backupPath: backupPath,\n existed: exists\n };\n \n if (exists) {\n // Copy the file to backup location\n await fs.copy(filePath, backupPath);\n }\n \n this.backups.push(backupEntry);\n }\n \n /**\n * Rollback all file changes by restoring from backups\n * \n * Restores all backed-up files to their original state. Files that didn't\n * exist before the operation are deleted. This operation is atomic - either\n * all files are restored or none are.\n * \n * @returns Promise that resolves when rollback is complete\n * @throws Error if any rollback operation fails\n */\n async rollback(): Promise<void> {\n if (!this.initialized) {\n return; // Nothing to rollback\n }\n \n const errors: string[] = [];\n \n // Process backups in reverse order (last modified first)\n for (const backup of this.backups.reverse()) {\n try {\n if (backup.existed) {\n // Restore the original file\n await fs.copy(backup.backupPath, backup.originalPath, { overwrite: true });\n } else {\n // File didn't exist before, remove it\n if (await fs.pathExists(backup.originalPath)) {\n await fs.remove(backup.originalPath);\n }\n }\n } catch (error) {\n const errorMsg = `Failed to rollback ${backup.originalPath}: ${error instanceof Error ? error.message : String(error)}`;\n errors.push(errorMsg);\n console.error(errorMsg);\n }\n }\n \n // Clean up backup directory\n try {\n await this.cleanup();\n } catch (error) {\n console.error('Failed to cleanup backup directory during rollback:', error);\n }\n \n if (errors.length > 0) {\n throw new Error(`Rollback completed with errors:\\n${errors.join('\\n')}`);\n }\n }\n \n /**\n * Clean up backup directory after successful operation\n * \n * Removes all temporary backup files and the backup directory. Should be\n * called after all operations complete successfully.\n * \n * @returns Promise that resolves when cleanup is complete\n */\n async cleanup(): Promise<void> {\n if (!this.initialized || !this.backupDir) {\n return;\n }\n \n try {\n await fs.remove(this.backupDir);\n } catch (error) {\n // Log but don't throw - cleanup errors shouldn't fail the operation\n console.warn(`Failed to cleanup backup directory ${this.backupDir}:`, error);\n }\n \n this.backups = [];\n this.initialized = false;\n this.backupDir = '';\n }\n \n /**\n * Get statistics about current backup session\n * \n * @returns Object containing backup statistics\n */\n getStats(): { totalBackups: number; backupDir: string; initialized: boolean } {\n return {\n totalBackups: this.backups.length,\n backupDir: this.backupDir,\n initialized: this.initialized\n };\n }\n}"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @fileoverview SQL Logger for capturing database operations during metadata sync
3
+ * @module sql-logger
4
+ *
5
+ * This module provides SQL logging functionality to capture all database operations
6
+ * during push commands. It supports both raw SQL logging and migration-formatted output.
7
+ */
8
+ import { SyncConfig } from '../config';
9
+ export interface SQLLoggerOptions {
10
+ enabled: boolean;
11
+ outputDirectory: string;
12
+ formatAsMigration: boolean;
13
+ }
14
+ export declare class SQLLogger {
15
+ private options;
16
+ private statements;
17
+ private isInitialized;
18
+ constructor(syncConfig: SyncConfig | null);
19
+ get enabled(): boolean;
20
+ /**
21
+ * Initialize the SQL logger and prepare output directory
22
+ */
23
+ initialize(): Promise<void>;
24
+ /**
25
+ * Log a SQL statement
26
+ */
27
+ logStatement(sql: string, params?: any[]): void;
28
+ /**
29
+ * Log a transaction boundary
30
+ */
31
+ logTransaction(action: 'BEGIN' | 'COMMIT' | 'ROLLBACK'): void;
32
+ /**
33
+ * Write the collected SQL statements to file
34
+ */
35
+ writeLog(): Promise<string | undefined>;
36
+ /**
37
+ * Clear all logged statements
38
+ */
39
+ clear(): void;
40
+ /**
41
+ * Format a parameter value for SQL
42
+ */
43
+ private formatParamValue;
44
+ }
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview SQL Logger for capturing database operations during metadata sync
4
+ * @module sql-logger
5
+ *
6
+ * This module provides SQL logging functionality to capture all database operations
7
+ * during push commands. It supports both raw SQL logging and migration-formatted output.
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.SQLLogger = void 0;
14
+ const fs_extra_1 = __importDefault(require("fs-extra"));
15
+ const path_1 = __importDefault(require("path"));
16
+ class SQLLogger {
17
+ options;
18
+ statements = [];
19
+ isInitialized = false;
20
+ constructor(syncConfig) {
21
+ this.options = {
22
+ enabled: syncConfig?.sqlLogging?.enabled || false,
23
+ outputDirectory: syncConfig?.sqlLogging?.outputDirectory || './sql_logging',
24
+ formatAsMigration: syncConfig?.sqlLogging?.formatAsMigration || false
25
+ };
26
+ }
27
+ get enabled() {
28
+ return this.options.enabled;
29
+ }
30
+ /**
31
+ * Initialize the SQL logger and prepare output directory
32
+ */
33
+ async initialize() {
34
+ if (!this.options.enabled || this.isInitialized) {
35
+ return;
36
+ }
37
+ // Ensure output directory exists
38
+ await fs_extra_1.default.ensureDir(this.options.outputDirectory);
39
+ this.isInitialized = true;
40
+ }
41
+ /**
42
+ * Log a SQL statement
43
+ */
44
+ logStatement(sql, params) {
45
+ if (!this.options.enabled) {
46
+ return;
47
+ }
48
+ // Format SQL with parameters inline for readability
49
+ let formattedSql = sql;
50
+ if (params && params.length > 0) {
51
+ // Replace parameter placeholders with actual values
52
+ params.forEach((param, index) => {
53
+ const placeholder = `@param${index + 1}`;
54
+ const value = this.formatParamValue(param);
55
+ formattedSql = formattedSql.replace(new RegExp(placeholder, 'g'), value);
56
+ });
57
+ }
58
+ this.statements.push(formattedSql);
59
+ }
60
+ /**
61
+ * Log a transaction boundary
62
+ */
63
+ logTransaction(action) {
64
+ if (!this.options.enabled) {
65
+ return;
66
+ }
67
+ this.statements.push(`${action} TRANSACTION;`);
68
+ }
69
+ /**
70
+ * Write the collected SQL statements to file
71
+ */
72
+ async writeLog() {
73
+ if (!this.options.enabled || this.statements.length === 0) {
74
+ return undefined;
75
+ }
76
+ await this.initialize();
77
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
78
+ let filename;
79
+ let content;
80
+ if (this.options.formatAsMigration) {
81
+ // Format as Flyway migration
82
+ const migrationTimestamp = timestamp.replace(/[-T]/g, '').substring(0, 14);
83
+ filename = `V${migrationTimestamp}__MetadataSync_Push.sql`;
84
+ content = [
85
+ '-- MemberJunction MetadataSync Push Migration',
86
+ `-- Generated at: ${new Date().toISOString()}`,
87
+ '-- Description: Metadata changes pushed via mj sync push command',
88
+ '',
89
+ '-- Note: Schema placeholders can be replaced during deployment',
90
+ '-- Replace ${flyway:defaultSchema} with your target schema name',
91
+ '',
92
+ ...this.statements.map(stmt => {
93
+ // Add schema placeholders for migration format
94
+ return stmt.replace(/(\[?)__mj(\]?)\./g, '${flyway:defaultSchema}.');
95
+ })
96
+ ].join('\n');
97
+ }
98
+ else {
99
+ // Regular SQL log format
100
+ filename = `metadatasync-push-${timestamp}.sql`;
101
+ content = [
102
+ '-- MemberJunction MetadataSync SQL Log',
103
+ `-- Generated at: ${new Date().toISOString()}`,
104
+ `-- Total statements: ${this.statements.length}`,
105
+ '',
106
+ ...this.statements
107
+ ].join('\n');
108
+ }
109
+ const filePath = path_1.default.join(this.options.outputDirectory, filename);
110
+ await fs_extra_1.default.writeFile(filePath, content, 'utf8');
111
+ return filePath;
112
+ }
113
+ /**
114
+ * Clear all logged statements
115
+ */
116
+ clear() {
117
+ this.statements = [];
118
+ }
119
+ /**
120
+ * Format a parameter value for SQL
121
+ */
122
+ formatParamValue(value) {
123
+ if (value === null) {
124
+ return 'NULL';
125
+ }
126
+ if (typeof value === 'string') {
127
+ // Escape single quotes and wrap in quotes
128
+ return `'${value.replace(/'/g, "''")}'`;
129
+ }
130
+ if (typeof value === 'boolean') {
131
+ return value ? '1' : '0';
132
+ }
133
+ if (value instanceof Date) {
134
+ return `'${value.toISOString()}'`;
135
+ }
136
+ return String(value);
137
+ }
138
+ }
139
+ exports.SQLLogger = SQLLogger;
140
+ //# sourceMappingURL=sql-logger.js.map