@memberjunction/metadata-sync 2.55.0 → 2.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -51
- package/dist/index.d.ts +21 -1
- package/dist/index.js +43 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/file-backup-manager.js +2 -2
- package/dist/lib/file-backup-manager.js.map +1 -1
- package/dist/lib/provider-utils.d.ts +2 -2
- package/dist/lib/provider-utils.js.map +1 -1
- package/dist/lib/sql-logger.d.ts +44 -0
- package/dist/lib/sql-logger.js +140 -0
- package/dist/lib/sql-logger.js.map +1 -0
- package/dist/lib/sync-engine.d.ts +25 -0
- package/dist/lib/sync-engine.js +72 -2
- package/dist/lib/sync-engine.js.map +1 -1
- package/dist/lib/transaction-manager.d.ts +35 -0
- package/dist/lib/transaction-manager.js +100 -0
- package/dist/lib/transaction-manager.js.map +1 -0
- package/dist/services/FileResetService.d.ts +30 -0
- package/dist/services/FileResetService.js +183 -0
- package/dist/services/FileResetService.js.map +1 -0
- package/dist/services/InitService.d.ts +17 -0
- package/dist/services/InitService.js +118 -0
- package/dist/services/InitService.js.map +1 -0
- package/dist/services/PullService.d.ts +45 -0
- package/dist/services/PullService.js +564 -0
- package/dist/services/PullService.js.map +1 -0
- package/dist/services/PushService.d.ts +47 -0
- package/dist/services/PushService.js +558 -0
- package/dist/services/PushService.js.map +1 -0
- package/dist/services/StatusService.d.ts +32 -0
- package/dist/services/StatusService.js +138 -0
- package/dist/services/StatusService.js.map +1 -0
- package/dist/services/WatchService.d.ts +34 -0
- package/dist/services/WatchService.js +296 -0
- package/dist/services/WatchService.js.map +1 -0
- package/dist/services/index.d.ts +16 -0
- package/dist/services/index.js +28 -0
- package/dist/services/index.js.map +1 -0
- package/package.json +14 -45
- package/bin/debug.js +0 -7
- package/bin/run +0 -17
- package/bin/run.js +0 -6
- package/dist/commands/file-reset/index.d.ts +0 -15
- package/dist/commands/file-reset/index.js +0 -221
- package/dist/commands/file-reset/index.js.map +0 -1
- package/dist/commands/init/index.d.ts +0 -7
- package/dist/commands/init/index.js +0 -155
- package/dist/commands/init/index.js.map +0 -1
- package/dist/commands/pull/index.d.ts +0 -246
- package/dist/commands/pull/index.js +0 -1448
- package/dist/commands/pull/index.js.map +0 -1
- package/dist/commands/push/index.d.ts +0 -41
- package/dist/commands/push/index.js +0 -1129
- package/dist/commands/push/index.js.map +0 -1
- package/dist/commands/status/index.d.ts +0 -10
- package/dist/commands/status/index.js +0 -199
- package/dist/commands/status/index.js.map +0 -1
- package/dist/commands/validate/index.d.ts +0 -15
- package/dist/commands/validate/index.js +0 -149
- package/dist/commands/validate/index.js.map +0 -1
- package/dist/commands/watch/index.d.ts +0 -15
- package/dist/commands/watch/index.js +0 -300
- package/dist/commands/watch/index.js.map +0 -1
- package/dist/hooks/init.d.ts +0 -3
- package/dist/hooks/init.js +0 -59
- package/dist/hooks/init.js.map +0 -1
- package/oclif.manifest.json +0 -376
package/README.md
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
# MemberJunction Metadata Sync
|
|
2
2
|
|
|
3
|
-
A
|
|
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
|
|
417
|
+
mj sync push --dir="templates" --dry-run
|
|
405
418
|
|
|
406
419
|
# If successful, do actual push
|
|
407
|
-
mj
|
|
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
|
|
916
|
+
mj sync validate
|
|
902
917
|
|
|
903
918
|
# Validate a specific directory
|
|
904
|
-
mj
|
|
919
|
+
mj sync validate --dir="./metadata"
|
|
905
920
|
|
|
906
921
|
# Validate with detailed output
|
|
907
|
-
mj
|
|
922
|
+
mj sync validate --verbose
|
|
908
923
|
|
|
909
924
|
# Validate with JSON output for CI/CD
|
|
910
|
-
mj
|
|
925
|
+
mj sync validate --format=json
|
|
911
926
|
|
|
912
927
|
# Save validation report to markdown file
|
|
913
|
-
mj
|
|
928
|
+
mj sync validate --save-report
|
|
914
929
|
|
|
915
930
|
# Initialize a directory for metadata sync
|
|
916
|
-
mj
|
|
931
|
+
mj sync init
|
|
917
932
|
|
|
918
933
|
# Pull all AI Prompts from database to ai-prompts directory
|
|
919
|
-
mj
|
|
934
|
+
mj sync pull --entity="AI Prompts"
|
|
920
935
|
|
|
921
936
|
# Pull specific records by filter
|
|
922
|
-
mj
|
|
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
|
|
926
|
-
mj
|
|
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
|
|
944
|
+
mj sync push
|
|
930
945
|
|
|
931
946
|
# Push only specific entity directory
|
|
932
|
-
mj
|
|
947
|
+
mj sync push --dir="ai-prompts"
|
|
933
948
|
|
|
934
949
|
# Push with verbose output (NEW)
|
|
935
|
-
mj
|
|
936
|
-
mj
|
|
950
|
+
mj sync push -v
|
|
951
|
+
mj sync push --verbose
|
|
937
952
|
|
|
938
953
|
# Dry run to see what would change
|
|
939
|
-
mj
|
|
954
|
+
mj sync push --dry-run
|
|
940
955
|
|
|
941
956
|
# Show status of local vs database
|
|
942
|
-
mj
|
|
957
|
+
mj sync status
|
|
943
958
|
|
|
944
959
|
# Watch for changes and auto-push
|
|
945
|
-
mj
|
|
960
|
+
mj sync watch
|
|
946
961
|
|
|
947
962
|
# CI/CD mode (push with no prompts, fails on validation errors)
|
|
948
|
-
mj
|
|
963
|
+
mj sync push --ci
|
|
949
964
|
|
|
950
965
|
# Push/Pull without validation
|
|
951
|
-
mj
|
|
952
|
-
mj
|
|
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
|
|
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.
|
|
1641
|
-
2.
|
|
1642
|
-
3.
|
|
1643
|
-
4.
|
|
1644
|
-
5.
|
|
1645
|
-
6.
|
|
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/
|
|
1659
|
-
mj
|
|
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
|
|
1695
|
-
mj
|
|
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
|
|
1721
|
+
mj sync validate
|
|
1703
1722
|
|
|
1704
1723
|
# Validate specific directory
|
|
1705
|
-
mj
|
|
1724
|
+
mj sync validate --dir="./metadata"
|
|
1706
1725
|
|
|
1707
1726
|
# Verbose output shows all files checked
|
|
1708
|
-
mj
|
|
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
|
|
1734
|
+
mj sync validate --format=json
|
|
1716
1735
|
|
|
1717
1736
|
# In CI mode, validation failures cause immediate exit
|
|
1718
|
-
mj
|
|
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
|
|
1734
|
-
mj
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1957
|
-
2. **Validate specific directories**: `mj
|
|
1958
|
-
3. **Reduce nesting depth warning**: `mj
|
|
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/
|
|
2078
|
-
npx mj
|
|
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,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
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
|
+
const core_entities_server_1 = require("@memberjunction/core-entities-server");
|
|
5
|
+
// Core library exports
|
|
6
6
|
var file_backup_manager_1 = require("./lib/file-backup-manager");
|
|
7
7
|
Object.defineProperty(exports, "FileBackupManager", { enumerable: true, get: function () { return file_backup_manager_1.FileBackupManager; } });
|
|
8
8
|
var sync_engine_1 = require("./lib/sync-engine");
|
|
9
9
|
Object.defineProperty(exports, "SyncEngine", { enumerable: true, get: function () { return sync_engine_1.SyncEngine; } });
|
|
10
|
+
var config_manager_1 = require("./lib/config-manager");
|
|
11
|
+
Object.defineProperty(exports, "ConfigManager", { enumerable: true, get: function () { return config_manager_1.ConfigManager; } });
|
|
12
|
+
Object.defineProperty(exports, "configManager", { enumerable: true, get: function () { return config_manager_1.configManager; } });
|
|
13
|
+
var singleton_manager_1 = require("./lib/singleton-manager");
|
|
14
|
+
Object.defineProperty(exports, "getSyncEngine", { enumerable: true, get: function () { return singleton_manager_1.getSyncEngine; } });
|
|
15
|
+
Object.defineProperty(exports, "resetSyncEngine", { enumerable: true, get: function () { return singleton_manager_1.resetSyncEngine; } });
|
|
16
|
+
var sql_logger_1 = require("./lib/sql-logger");
|
|
17
|
+
Object.defineProperty(exports, "SQLLogger", { enumerable: true, get: function () { return sql_logger_1.SQLLogger; } });
|
|
18
|
+
var transaction_manager_1 = require("./lib/transaction-manager");
|
|
19
|
+
Object.defineProperty(exports, "TransactionManager", { enumerable: true, get: function () { return transaction_manager_1.TransactionManager; } });
|
|
20
|
+
// Service exports
|
|
21
|
+
var InitService_1 = require("./services/InitService");
|
|
22
|
+
Object.defineProperty(exports, "InitService", { enumerable: true, get: function () { return InitService_1.InitService; } });
|
|
23
|
+
var PullService_1 = require("./services/PullService");
|
|
24
|
+
Object.defineProperty(exports, "PullService", { enumerable: true, get: function () { return PullService_1.PullService; } });
|
|
25
|
+
var PushService_1 = require("./services/PushService");
|
|
26
|
+
Object.defineProperty(exports, "PushService", { enumerable: true, get: function () { return PushService_1.PushService; } });
|
|
27
|
+
var StatusService_1 = require("./services/StatusService");
|
|
28
|
+
Object.defineProperty(exports, "StatusService", { enumerable: true, get: function () { return StatusService_1.StatusService; } });
|
|
29
|
+
var FileResetService_1 = require("./services/FileResetService");
|
|
30
|
+
Object.defineProperty(exports, "FileResetService", { enumerable: true, get: function () { return FileResetService_1.FileResetService; } });
|
|
31
|
+
var WatchService_1 = require("./services/WatchService");
|
|
32
|
+
Object.defineProperty(exports, "WatchService", { enumerable: true, get: function () { return WatchService_1.WatchService; } });
|
|
33
|
+
var ValidationService_1 = require("./services/ValidationService");
|
|
34
|
+
Object.defineProperty(exports, "ValidationService", { enumerable: true, get: function () { return ValidationService_1.ValidationService; } });
|
|
35
|
+
var FormattingService_1 = require("./services/FormattingService");
|
|
36
|
+
Object.defineProperty(exports, "FormattingService", { enumerable: true, get: function () { return FormattingService_1.FormattingService; } });
|
|
37
|
+
// Configuration types
|
|
38
|
+
var config_1 = require("./config");
|
|
39
|
+
Object.defineProperty(exports, "loadMJConfig", { enumerable: true, get: function () { return config_1.loadMJConfig; } });
|
|
40
|
+
Object.defineProperty(exports, "loadSyncConfig", { enumerable: true, get: function () { return config_1.loadSyncConfig; } });
|
|
41
|
+
Object.defineProperty(exports, "loadEntityConfig", { enumerable: true, get: function () { return config_1.loadEntityConfig; } });
|
|
42
|
+
Object.defineProperty(exports, "loadFolderConfig", { enumerable: true, get: function () { return config_1.loadFolderConfig; } });
|
|
43
|
+
// Provider utilities
|
|
44
|
+
var provider_utils_1 = require("./lib/provider-utils");
|
|
45
|
+
Object.defineProperty(exports, "initializeProvider", { enumerable: true, get: function () { return provider_utils_1.initializeProvider; } });
|
|
46
|
+
Object.defineProperty(exports, "getSystemUser", { enumerable: true, get: function () { return provider_utils_1.getSystemUser; } });
|
|
47
|
+
Object.defineProperty(exports, "findEntityDirectories", { enumerable: true, get: function () { return provider_utils_1.findEntityDirectories; } });
|
|
48
|
+
Object.defineProperty(exports, "getDataProvider", { enumerable: true, get: function () { return provider_utils_1.getDataProvider; } });
|
|
49
|
+
(0, core_entities_server_1.LoadAIPromptEntityExtendedServerSubClass)();
|
|
10
50
|
//# 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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+EAAgG;AAEhG,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;AAejB,IAAA,+DAAwC,GAAE,CAAC","sourcesContent":["import { LoadAIPromptEntityExtendedServerSubClass } from '@memberjunction/core-entities-server';\n\n// 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';\n\nLoadAIPromptEntityExtendedServerSubClass();"]}
|
|
@@ -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
|
|
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,
|
|
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":";AAAA;;;;;;;GAOG;;;;;;AAEH,wDAA0B;AAC1B,gDAAwB;AACxB,4CAAoB;AACpB
|
|
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}"]}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
|
|
10
10
|
import type { MJConfig } from '../config';
|
|
11
|
-
import { UserInfo } from '@memberjunction/core';
|
|
11
|
+
import { DatabaseProviderBase, UserInfo } from '@memberjunction/core';
|
|
12
12
|
/**
|
|
13
13
|
* Initialize a SQLServerDataProvider with the given configuration
|
|
14
14
|
*
|
|
@@ -79,7 +79,7 @@ export declare function getSystemUser(): UserInfo;
|
|
|
79
79
|
* }
|
|
80
80
|
* ```
|
|
81
81
|
*/
|
|
82
|
-
export declare function getDataProvider():
|
|
82
|
+
export declare function getDataProvider(): DatabaseProviderBase | null;
|
|
83
83
|
/**
|
|
84
84
|
* Find entity directories at the immediate level only
|
|
85
85
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-utils.js","sourceRoot":"","sources":["../../src/lib/provider-utils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Database provider utilities for MetadataSync\n * @module provider-utils\n * \n * This module provides utilities for initializing and managing the database\n * connection, accessing system users, and finding entity directories. It handles\n * the mssql ConnectionPool lifecycle and MemberJunction provider initialization.\n */\n\nimport * as sql from 'mssql';\nimport { SQLServerDataProvider, SQLServerProviderConfigData, UserCache, setupSQLServerClient } from '@memberjunction/sqlserver-dataprovider';\nimport type { MJConfig } from '../config';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { UserInfo } from '@memberjunction/core';\n\n/** Global ConnectionPool instance for connection lifecycle management */\nlet globalPool: sql.ConnectionPool | null = null;\n\n/** Global provider instance to ensure single initialization */\nlet globalProvider: SQLServerDataProvider | null = null;\n\n/** Promise to track ongoing initialization */\nlet initializationPromise: Promise<SQLServerDataProvider> | null = null;\n\n/**\n * Initialize a SQLServerDataProvider with the given configuration\n * \n * Creates and initializes a mssql ConnectionPool for SQL Server, then sets up\n * the MemberJunction SQLServerDataProvider. The connection is stored globally\n * for proper cleanup. Auto-detects Azure SQL databases for encryption settings.\n * \n * @param config - MemberJunction configuration with database connection details\n * @returns Promise resolving to initialized SQLServerDataProvider instance\n * @throws Error if database connection fails\n * \n * @example\n * ```typescript\n * const config = loadMJConfig();\n * const provider = await initializeProvider(config);\n * // Provider is ready for use\n * ```\n */\nexport async function initializeProvider(config: MJConfig): Promise<SQLServerDataProvider> {\n // Return existing provider if already initialized\n if (globalProvider) {\n return globalProvider;\n }\n \n // Return ongoing initialization if in progress\n if (initializationPromise) {\n return initializationPromise;\n }\n \n // Start new initialization\n initializationPromise = (async () => {\n // Create mssql config\n const poolConfig: sql.config = {\n server: config.dbHost,\n port: config.dbPort ? Number(config.dbPort) : 1433,\n database: config.dbDatabase,\n user: config.dbUsername,\n password: config.dbPassword,\n options: {\n encrypt: config.dbEncrypt === 'Y' || config.dbEncrypt === 'true' || \n config.dbHost.includes('.database.windows.net'), // Auto-detect Azure SQL\n trustServerCertificate: config.dbTrustServerCertificate === 'Y',\n instanceName: config.dbInstanceName,\n enableArithAbort: true\n }\n };\n \n // Create and connect pool\n const pool = new sql.ConnectionPool(poolConfig);\n await pool.connect();\n \n // Store for cleanup\n globalPool = pool;\n \n // Create provider config\n const providerConfig = new SQLServerProviderConfigData(\n pool,\n config.mjCoreSchema || '__mj' \n );\n \n // Use setupSQLServerClient to properly initialize\n globalProvider = await setupSQLServerClient(providerConfig);\n return globalProvider;\n })();\n \n return initializationPromise;\n}\n\n/**\n * Clean up the global database connection\n * \n * Closes the mssql ConnectionPool if it exists and is connected.\n * Should be called when the CLI command completes to ensure proper cleanup.\n * \n * @returns Promise that resolves when cleanup is complete\n * \n * @example\n * ```typescript\n * try {\n * // Do work with database\n * } finally {\n * await cleanupProvider();\n * }\n * ```\n */\nexport async function cleanupProvider(): Promise<void> {\n if (globalPool && globalPool.connected) {\n await globalPool.close();\n globalPool = null;\n }\n globalProvider = null;\n initializationPromise = null;\n}\n\n/**\n * Get the system user from the UserCache\n * \n * Retrieves the \"System\" user from MemberJunction's UserCache. This user is\n * typically used for CLI operations where no specific user context exists.\n * The System user must have the Developer role to perform metadata sync operations.\n * \n * @returns The System UserInfo object\n * @throws Error if System user is not found in the cache or doesn't have Developer role\n * \n * @example\n * ```typescript\n * const systemUser = getSystemUser();\n * const syncEngine = new SyncEngine(systemUser);\n * ```\n */\nexport function getSystemUser(): UserInfo {\n const sysUser = UserCache.Instance.UserByName(\"System\", false);\n if (!sysUser) {\n throw new Error(\"System user not found in cache. Ensure the system user exists in the database.\"); \n }\n \n // Check if the System user has the Developer role\n const hasDeveloperRole = sysUser.UserRoles && sysUser.UserRoles.some(\n userRole => userRole.Role.trim().toLowerCase() === 'developer'\n );\n \n if (!hasDeveloperRole) {\n throw new Error(\n \"System user does not have the 'Developer' role. \" +\n \"The Developer role is required for metadata sync operations. \" +\n \"Please ensure the System user is assigned the Developer role in the database:\\n\" +\n \"* Add a record to the __mj.UserRole table linking the System user to the Developer role\"\n );\n }\n \n return sysUser;\n}\n\n/**\n * Get the current data provider instance\n * \n * Returns the global SQLServerDataProvider instance that was initialized by\n * initializeProvider. This allows access to data provider features like SQL logging.\n * \n * @returns The global SQLServerDataProvider instance or null if not initialized\n * \n * @example\n * ```typescript\n * const provider = getDataProvider();\n * if (provider?.CreateSqlLogger) {\n * const logger = await provider.CreateSqlLogger('/path/to/log.sql');\n * }\n * ```\n */\nexport function getDataProvider(): SQLServerDataProvider | null {\n return globalProvider;\n}\n\n/**\n * Find entity directories at the immediate level only\n * \n * Searches for directories containing .mj-sync.json files, which indicate\n * entity data directories. Only searches immediate subdirectories, not recursive.\n * If a specific directory is provided, only checks that directory.\n * \n * @param dir - Base directory to search from\n * @param specificDir - Optional specific subdirectory name to check\n * @param directoryOrder - Optional array specifying the order directories should be processed\n * @param ignoreDirectories - Optional array of directory patterns to ignore\n * @returns Array of absolute directory paths containing .mj-sync.json files, ordered according to directoryOrder\n * \n * @example\n * ```typescript\n * // Find all entity directories\n * const dirs = findEntityDirectories(process.cwd());\n * \n * // Check specific directory\n * const dirs = findEntityDirectories(process.cwd(), 'ai-prompts');\n * \n * // Find directories with custom ordering\n * const dirs = findEntityDirectories(process.cwd(), undefined, ['prompts', 'agent-types']);\n * ```\n */\nexport function findEntityDirectories(dir: string, specificDir?: string, directoryOrder?: string[], ignoreDirectories?: string[]): string[] {\n const results: string[] = [];\n \n // If specific directory is provided, check if it's an entity directory or root config directory\n if (specificDir) {\n const targetDir = path.isAbsolute(specificDir) ? specificDir : path.join(dir, specificDir);\n if (fs.existsSync(targetDir)) {\n const syncConfigPath = path.join(targetDir, '.mj-sync.json');\n const hasSyncConfig = fs.existsSync(syncConfigPath);\n \n if (hasSyncConfig) {\n try {\n const config = JSON.parse(fs.readFileSync(syncConfigPath, 'utf8'));\n \n // If this config has an entity field, it's an entity directory\n if (config.entity) {\n results.push(targetDir);\n return results;\n }\n \n // If this config has directoryOrder but no entity, treat it as a root config\n // and look for entity directories in its subdirectories\n if (config.directoryOrder) {\n // Merge ignore directories from parent with current config\n const mergedIgnoreDirectories = [\n ...(ignoreDirectories || []),\n ...(config.ignoreDirectories || [])\n ];\n return findEntityDirectories(targetDir, undefined, config.directoryOrder, mergedIgnoreDirectories);\n }\n } catch (error) {\n // If we can't parse the config, treat it as a regular directory\n }\n }\n \n // Fallback: look for entity subdirectories in the target directory\n return findEntityDirectories(targetDir, undefined, directoryOrder, ignoreDirectories);\n }\n return results;\n }\n \n // Otherwise, find all immediate subdirectories with .mj-sync.json\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n const foundDirectories: string[] = [];\n \n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.')) {\n // Check if this directory should be ignored\n if (ignoreDirectories && ignoreDirectories.some(pattern => {\n // Simple pattern matching: exact name or ends with pattern\n return entry.name === pattern || entry.name.endsWith(pattern);\n })) {\n continue;\n }\n \n const subDir = path.join(dir, entry.name);\n const hasSyncConfig = fs.existsSync(path.join(subDir, '.mj-sync.json'));\n \n if (hasSyncConfig) {\n foundDirectories.push(subDir);\n }\n }\n }\n \n // If directoryOrder is specified, sort directories according to it\n if (directoryOrder && directoryOrder.length > 0) {\n const orderedDirs: string[] = [];\n const unorderedDirs: string[] = [];\n \n // First, add directories in the specified order\n for (const dirName of directoryOrder) {\n const matchingDir = foundDirectories.find(fullPath => \n path.basename(fullPath) === dirName\n );\n if (matchingDir) {\n orderedDirs.push(matchingDir);\n }\n }\n \n // Then, add any remaining directories in alphabetical order\n for (const foundDir of foundDirectories) {\n const dirName = path.basename(foundDir);\n if (!directoryOrder.includes(dirName)) {\n unorderedDirs.push(foundDir);\n }\n }\n \n // Sort unordered directories alphabetically\n unorderedDirs.sort((a, b) => path.basename(a).localeCompare(path.basename(b)));\n \n return [...orderedDirs, ...unorderedDirs];\n }\n \n // No ordering specified, return in alphabetical order (existing behavior)\n return foundDirectories.sort((a, b) => path.basename(a).localeCompare(path.basename(b)));\n}"]}
|
|
1
|
+
{"version":3,"file":"provider-utils.js","sourceRoot":"","sources":["../../src/lib/provider-utils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Database provider utilities for MetadataSync\n * @module provider-utils\n * \n * This module provides utilities for initializing and managing the database\n * connection, accessing system users, and finding entity directories. It handles\n * the mssql ConnectionPool lifecycle and MemberJunction provider initialization.\n */\n\nimport * as sql from 'mssql';\nimport { SQLServerDataProvider, SQLServerProviderConfigData, UserCache, setupSQLServerClient } from '@memberjunction/sqlserver-dataprovider';\nimport type { MJConfig } from '../config';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { DatabaseProviderBase, UserInfo } from '@memberjunction/core';\n\n/** Global ConnectionPool instance for connection lifecycle management */\nlet globalPool: sql.ConnectionPool | null = null;\n\n/** Global provider instance to ensure single initialization */\nlet globalProvider: SQLServerDataProvider | null = null;\n\n/** Promise to track ongoing initialization */\nlet initializationPromise: Promise<SQLServerDataProvider> | null = null;\n\n/**\n * Initialize a SQLServerDataProvider with the given configuration\n * \n * Creates and initializes a mssql ConnectionPool for SQL Server, then sets up\n * the MemberJunction SQLServerDataProvider. The connection is stored globally\n * for proper cleanup. Auto-detects Azure SQL databases for encryption settings.\n * \n * @param config - MemberJunction configuration with database connection details\n * @returns Promise resolving to initialized SQLServerDataProvider instance\n * @throws Error if database connection fails\n * \n * @example\n * ```typescript\n * const config = loadMJConfig();\n * const provider = await initializeProvider(config);\n * // Provider is ready for use\n * ```\n */\nexport async function initializeProvider(config: MJConfig): Promise<SQLServerDataProvider> {\n // Return existing provider if already initialized\n if (globalProvider) {\n return globalProvider;\n }\n \n // Return ongoing initialization if in progress\n if (initializationPromise) {\n return initializationPromise;\n }\n \n // Start new initialization\n initializationPromise = (async () => {\n // Create mssql config\n const poolConfig: sql.config = {\n server: config.dbHost,\n port: config.dbPort ? Number(config.dbPort) : 1433,\n database: config.dbDatabase,\n user: config.dbUsername,\n password: config.dbPassword,\n options: {\n encrypt: config.dbEncrypt === 'Y' || config.dbEncrypt === 'true' || \n config.dbHost.includes('.database.windows.net'), // Auto-detect Azure SQL\n trustServerCertificate: config.dbTrustServerCertificate === 'Y',\n instanceName: config.dbInstanceName,\n enableArithAbort: true\n }\n };\n \n // Create and connect pool\n const pool = new sql.ConnectionPool(poolConfig);\n await pool.connect();\n \n // Store for cleanup\n globalPool = pool;\n \n // Create provider config\n const providerConfig = new SQLServerProviderConfigData(\n pool,\n config.mjCoreSchema || '__mj' \n );\n \n // Use setupSQLServerClient to properly initialize\n globalProvider = await setupSQLServerClient(providerConfig);\n return globalProvider;\n })();\n \n return initializationPromise;\n}\n\n/**\n * Clean up the global database connection\n * \n * Closes the mssql ConnectionPool if it exists and is connected.\n * Should be called when the CLI command completes to ensure proper cleanup.\n * \n * @returns Promise that resolves when cleanup is complete\n * \n * @example\n * ```typescript\n * try {\n * // Do work with database\n * } finally {\n * await cleanupProvider();\n * }\n * ```\n */\nexport async function cleanupProvider(): Promise<void> {\n if (globalPool && globalPool.connected) {\n await globalPool.close();\n globalPool = null;\n }\n globalProvider = null;\n initializationPromise = null;\n}\n\n/**\n * Get the system user from the UserCache\n * \n * Retrieves the \"System\" user from MemberJunction's UserCache. This user is\n * typically used for CLI operations where no specific user context exists.\n * The System user must have the Developer role to perform metadata sync operations.\n * \n * @returns The System UserInfo object\n * @throws Error if System user is not found in the cache or doesn't have Developer role\n * \n * @example\n * ```typescript\n * const systemUser = getSystemUser();\n * const syncEngine = new SyncEngine(systemUser);\n * ```\n */\nexport function getSystemUser(): UserInfo {\n const sysUser = UserCache.Instance.UserByName(\"System\", false);\n if (!sysUser) {\n throw new Error(\"System user not found in cache. Ensure the system user exists in the database.\"); \n }\n \n // Check if the System user has the Developer role\n const hasDeveloperRole = sysUser.UserRoles && sysUser.UserRoles.some(\n userRole => userRole.Role.trim().toLowerCase() === 'developer'\n );\n \n if (!hasDeveloperRole) {\n throw new Error(\n \"System user does not have the 'Developer' role. \" +\n \"The Developer role is required for metadata sync operations. \" +\n \"Please ensure the System user is assigned the Developer role in the database:\\n\" +\n \"* Add a record to the __mj.UserRole table linking the System user to the Developer role\"\n );\n }\n \n return sysUser;\n}\n\n/**\n * Get the current data provider instance\n * \n * Returns the global SQLServerDataProvider instance that was initialized by\n * initializeProvider. This allows access to data provider features like SQL logging.\n * \n * @returns The global SQLServerDataProvider instance or null if not initialized\n * \n * @example\n * ```typescript\n * const provider = getDataProvider();\n * if (provider?.CreateSqlLogger) {\n * const logger = await provider.CreateSqlLogger('/path/to/log.sql');\n * }\n * ```\n */\nexport function getDataProvider(): DatabaseProviderBase | null {\n return globalProvider;\n}\n\n/**\n * Find entity directories at the immediate level only\n * \n * Searches for directories containing .mj-sync.json files, which indicate\n * entity data directories. Only searches immediate subdirectories, not recursive.\n * If a specific directory is provided, only checks that directory.\n * \n * @param dir - Base directory to search from\n * @param specificDir - Optional specific subdirectory name to check\n * @param directoryOrder - Optional array specifying the order directories should be processed\n * @param ignoreDirectories - Optional array of directory patterns to ignore\n * @returns Array of absolute directory paths containing .mj-sync.json files, ordered according to directoryOrder\n * \n * @example\n * ```typescript\n * // Find all entity directories\n * const dirs = findEntityDirectories(process.cwd());\n * \n * // Check specific directory\n * const dirs = findEntityDirectories(process.cwd(), 'ai-prompts');\n * \n * // Find directories with custom ordering\n * const dirs = findEntityDirectories(process.cwd(), undefined, ['prompts', 'agent-types']);\n * ```\n */\nexport function findEntityDirectories(dir: string, specificDir?: string, directoryOrder?: string[], ignoreDirectories?: string[]): string[] {\n const results: string[] = [];\n \n // If specific directory is provided, check if it's an entity directory or root config directory\n if (specificDir) {\n const targetDir = path.isAbsolute(specificDir) ? specificDir : path.join(dir, specificDir);\n if (fs.existsSync(targetDir)) {\n const syncConfigPath = path.join(targetDir, '.mj-sync.json');\n const hasSyncConfig = fs.existsSync(syncConfigPath);\n \n if (hasSyncConfig) {\n try {\n const config = JSON.parse(fs.readFileSync(syncConfigPath, 'utf8'));\n \n // If this config has an entity field, it's an entity directory\n if (config.entity) {\n results.push(targetDir);\n return results;\n }\n \n // If this config has directoryOrder but no entity, treat it as a root config\n // and look for entity directories in its subdirectories\n if (config.directoryOrder) {\n // Merge ignore directories from parent with current config\n const mergedIgnoreDirectories = [\n ...(ignoreDirectories || []),\n ...(config.ignoreDirectories || [])\n ];\n return findEntityDirectories(targetDir, undefined, config.directoryOrder, mergedIgnoreDirectories);\n }\n } catch (error) {\n // If we can't parse the config, treat it as a regular directory\n }\n }\n \n // Fallback: look for entity subdirectories in the target directory\n return findEntityDirectories(targetDir, undefined, directoryOrder, ignoreDirectories);\n }\n return results;\n }\n \n // Otherwise, find all immediate subdirectories with .mj-sync.json\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n const foundDirectories: string[] = [];\n \n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.')) {\n // Check if this directory should be ignored\n if (ignoreDirectories && ignoreDirectories.some(pattern => {\n // Simple pattern matching: exact name or ends with pattern\n return entry.name === pattern || entry.name.endsWith(pattern);\n })) {\n continue;\n }\n \n const subDir = path.join(dir, entry.name);\n const hasSyncConfig = fs.existsSync(path.join(subDir, '.mj-sync.json'));\n \n if (hasSyncConfig) {\n foundDirectories.push(subDir);\n }\n }\n }\n \n // If directoryOrder is specified, sort directories according to it\n if (directoryOrder && directoryOrder.length > 0) {\n const orderedDirs: string[] = [];\n const unorderedDirs: string[] = [];\n \n // First, add directories in the specified order\n for (const dirName of directoryOrder) {\n const matchingDir = foundDirectories.find(fullPath => \n path.basename(fullPath) === dirName\n );\n if (matchingDir) {\n orderedDirs.push(matchingDir);\n }\n }\n \n // Then, add any remaining directories in alphabetical order\n for (const foundDir of foundDirectories) {\n const dirName = path.basename(foundDir);\n if (!directoryOrder.includes(dirName)) {\n unorderedDirs.push(foundDir);\n }\n }\n \n // Sort unordered directories alphabetically\n unorderedDirs.sort((a, b) => path.basename(a).localeCompare(path.basename(b)));\n \n return [...orderedDirs, ...unorderedDirs];\n }\n \n // No ordering specified, return in alphabetical order (existing behavior)\n return foundDirectories.sort((a, b) => path.basename(a).localeCompare(path.basename(b)));\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
|
+
}
|