@memberjunction/metadata-sync 2.46.0 → 2.48.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 +341 -28
- package/dist/commands/pull/index.d.ts +220 -0
- package/dist/commands/pull/index.js +1094 -113
- package/dist/commands/pull/index.js.map +1 -1
- package/dist/commands/push/index.d.ts +1 -0
- package/dist/commands/push/index.js +90 -40
- package/dist/commands/push/index.js.map +1 -1
- package/dist/commands/status/index.js +51 -7
- package/dist/commands/status/index.js.map +1 -1
- package/dist/commands/watch/index.js +20 -7
- package/dist/commands/watch/index.js.map +1 -1
- package/dist/config.d.ts +210 -0
- package/dist/config.js +83 -13
- package/dist/config.js.map +1 -1
- package/dist/hooks/init.js +9 -1
- package/dist/hooks/init.js.map +1 -1
- package/dist/lib/config-manager.d.ts +56 -0
- package/dist/lib/config-manager.js +104 -0
- package/dist/lib/config-manager.js.map +1 -0
- package/dist/lib/provider-utils.d.ts +76 -4
- package/dist/lib/provider-utils.js +136 -52
- package/dist/lib/provider-utils.js.map +1 -1
- package/dist/lib/singleton-manager.d.ts +34 -0
- package/dist/lib/singleton-manager.js +62 -0
- package/dist/lib/singleton-manager.js.map +1 -0
- package/dist/lib/sync-engine.d.ts +239 -5
- package/dist/lib/sync-engine.js +314 -5
- package/dist/lib/sync-engine.js.map +1 -1
- package/oclif.manifest.json +51 -37
- package/package.json +6 -6
package/dist/config.d.ts
CHANGED
|
@@ -1,44 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configuration types and loaders for MetadataSync
|
|
3
|
+
* @module config
|
|
4
|
+
*
|
|
5
|
+
* This module defines configuration interfaces and provides utilities for loading
|
|
6
|
+
* various configuration files used by the MetadataSync tool. It supports:
|
|
7
|
+
* - MemberJunction database configuration (mj.config.cjs)
|
|
8
|
+
* - Sync configuration (.mj-sync.json)
|
|
9
|
+
* - Entity-specific configuration (.mj-sync.json with entity field)
|
|
10
|
+
* - Folder-level defaults (.mj-folder.json)
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* MemberJunction database configuration
|
|
14
|
+
*
|
|
15
|
+
* Defines connection parameters and settings for connecting to the MemberJunction
|
|
16
|
+
* database. Typically loaded from mj.config.cjs in the project root.
|
|
17
|
+
*/
|
|
1
18
|
export interface MJConfig {
|
|
19
|
+
/** Database server hostname or IP address */
|
|
2
20
|
dbHost: string;
|
|
21
|
+
/** Database server port (defaults to 1433 for SQL Server) */
|
|
3
22
|
dbPort?: number;
|
|
23
|
+
/** Database name to connect to */
|
|
4
24
|
dbDatabase: string;
|
|
25
|
+
/** Database authentication username */
|
|
5
26
|
dbUsername: string;
|
|
27
|
+
/** Database authentication password */
|
|
6
28
|
dbPassword: string;
|
|
29
|
+
/** Whether to trust the server certificate (Y/N) */
|
|
7
30
|
dbTrustServerCertificate?: string;
|
|
31
|
+
/** Whether to encrypt the connection (Y/N, auto-detected for Azure SQL) */
|
|
32
|
+
dbEncrypt?: string;
|
|
33
|
+
/** SQL Server instance name (for named instances) */
|
|
8
34
|
dbInstanceName?: string;
|
|
35
|
+
/** Schema name for MemberJunction core tables (defaults to __mj) */
|
|
9
36
|
mjCoreSchema?: string;
|
|
37
|
+
/** Allow additional properties for extensibility */
|
|
10
38
|
[key: string]: any;
|
|
11
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Global sync configuration
|
|
42
|
+
*
|
|
43
|
+
* Defines settings that apply to the entire sync process, including push/pull
|
|
44
|
+
* behaviors and watch mode configuration. Stored in .mj-sync.json at the root.
|
|
45
|
+
*/
|
|
12
46
|
export interface SyncConfig {
|
|
47
|
+
/** Version of the sync configuration format */
|
|
13
48
|
version: string;
|
|
49
|
+
/** Push command configuration */
|
|
14
50
|
push?: {
|
|
51
|
+
/** Whether to validate records before pushing to database */
|
|
15
52
|
validateBeforePush?: boolean;
|
|
53
|
+
/** Whether to require user confirmation before push */
|
|
16
54
|
requireConfirmation?: boolean;
|
|
17
55
|
};
|
|
56
|
+
/** Watch command configuration */
|
|
18
57
|
watch?: {
|
|
58
|
+
/** Milliseconds to wait before processing file changes */
|
|
19
59
|
debounceMs?: number;
|
|
60
|
+
/** File patterns to ignore during watch */
|
|
20
61
|
ignorePatterns?: string[];
|
|
21
62
|
};
|
|
22
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Configuration for related entity synchronization
|
|
66
|
+
*
|
|
67
|
+
* Defines how to pull and push related entities that have foreign key relationships
|
|
68
|
+
* with a parent entity. Supports nested relationships for deep object graphs.
|
|
69
|
+
*/
|
|
23
70
|
export interface RelatedEntityConfig {
|
|
71
|
+
/** Name of the related entity to sync */
|
|
24
72
|
entity: string;
|
|
73
|
+
/** Field name that contains the foreign key reference to parent (e.g., "PromptID") */
|
|
25
74
|
foreignKey: string;
|
|
75
|
+
/** Optional SQL filter to apply when pulling related records */
|
|
26
76
|
filter?: string;
|
|
77
|
+
/** Fields to externalize to separate files for this related entity */
|
|
78
|
+
externalizeFields?: string[] | {
|
|
79
|
+
[fieldName: string]: {
|
|
80
|
+
/** File extension to use (e.g., ".md", ".txt", ".html") */
|
|
81
|
+
extension?: string;
|
|
82
|
+
};
|
|
83
|
+
} | Array<{
|
|
84
|
+
/** Field name to externalize */
|
|
85
|
+
field: string;
|
|
86
|
+
/** Pattern for the output file. Supports placeholders from the entity */
|
|
87
|
+
pattern: string;
|
|
88
|
+
}>;
|
|
89
|
+
/** Fields to exclude from the pulled data for this related entity */
|
|
90
|
+
excludeFields?: string[];
|
|
91
|
+
/** Foreign key fields to convert to @lookup references for this related entity */
|
|
92
|
+
lookupFields?: {
|
|
93
|
+
[fieldName: string]: {
|
|
94
|
+
entity: string;
|
|
95
|
+
field: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
/** Nested related entities for deep object graphs */
|
|
27
99
|
relatedEntities?: Record<string, RelatedEntityConfig>;
|
|
28
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Entity-specific configuration
|
|
103
|
+
*
|
|
104
|
+
* Defines settings for a specific entity type within a directory. Stored in
|
|
105
|
+
* .mj-sync.json files that contain an "entity" field. Supports defaults,
|
|
106
|
+
* file patterns, and related entity configuration.
|
|
107
|
+
*/
|
|
29
108
|
export interface EntityConfig {
|
|
109
|
+
/** Name of the entity this directory contains */
|
|
30
110
|
entity: string;
|
|
111
|
+
/** Glob pattern for finding data files (defaults to "*.json") */
|
|
31
112
|
filePattern?: string;
|
|
113
|
+
/** Default field values applied to all records in this directory */
|
|
32
114
|
defaults?: Record<string, any>;
|
|
115
|
+
/** Pull command specific configuration */
|
|
33
116
|
pull?: {
|
|
117
|
+
/** Glob pattern for finding existing files to update (defaults to filePattern) */
|
|
118
|
+
filePattern?: string;
|
|
119
|
+
/** Whether to create new files for records not found locally */
|
|
120
|
+
createNewFileIfNotFound?: boolean;
|
|
121
|
+
/** Filename for new records when createNewFileIfNotFound is true */
|
|
122
|
+
newFileName?: string;
|
|
123
|
+
/** Whether to append multiple new records to a single file */
|
|
124
|
+
appendRecordsToExistingFile?: boolean;
|
|
125
|
+
/** Whether to update existing records found in local files */
|
|
126
|
+
updateExistingRecords?: boolean;
|
|
127
|
+
/** Fields to preserve during updates (never overwrite these) */
|
|
128
|
+
preserveFields?: string[];
|
|
129
|
+
/** Strategy for merging updates: "overwrite" | "merge" | "skip" */
|
|
130
|
+
mergeStrategy?: 'overwrite' | 'merge' | 'skip';
|
|
131
|
+
/** Create backup files before updating existing files */
|
|
132
|
+
backupBeforeUpdate?: boolean;
|
|
133
|
+
/** Directory name for backup files (defaults to ".backups") */
|
|
134
|
+
backupDirectory?: string;
|
|
135
|
+
/** SQL filter to apply when pulling records from database */
|
|
34
136
|
filter?: string;
|
|
137
|
+
/** Configuration for pulling related entities */
|
|
35
138
|
relatedEntities?: Record<string, RelatedEntityConfig>;
|
|
139
|
+
/** Fields to externalize to separate files with optional configuration */
|
|
140
|
+
externalizeFields?: string[] | {
|
|
141
|
+
[fieldName: string]: {
|
|
142
|
+
/** File extension to use (e.g., ".md", ".txt", ".html") */
|
|
143
|
+
extension?: string;
|
|
144
|
+
};
|
|
145
|
+
} | Array<{
|
|
146
|
+
/** Field name to externalize */
|
|
147
|
+
field: string;
|
|
148
|
+
/** Pattern for the output file. Supports placeholders:
|
|
149
|
+
* - {Name}: Entity's name field value
|
|
150
|
+
* - {ID}: Entity's ID
|
|
151
|
+
* - {FieldName}: The field being externalized
|
|
152
|
+
* - Any other {FieldName} from the entity
|
|
153
|
+
* Example: "@file:templates/{Name}.template.md"
|
|
154
|
+
*/
|
|
155
|
+
pattern: string;
|
|
156
|
+
}>;
|
|
157
|
+
/** Fields to exclude from the pulled data (e.g., ["TemplateID"]) */
|
|
158
|
+
excludeFields?: string[];
|
|
159
|
+
/** Foreign key fields to convert to @lookup references */
|
|
160
|
+
lookupFields?: {
|
|
161
|
+
/** Field name in this entity (e.g., "CategoryID") */
|
|
162
|
+
[fieldName: string]: {
|
|
163
|
+
/** Target entity name (e.g., "AI Prompt Categories") */
|
|
164
|
+
entity: string;
|
|
165
|
+
/** Field in target entity to use for lookup (e.g., "Name") */
|
|
166
|
+
field: string;
|
|
167
|
+
};
|
|
168
|
+
};
|
|
36
169
|
};
|
|
37
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Folder-level configuration
|
|
173
|
+
*
|
|
174
|
+
* Defines default values that cascade down to all subdirectories. Stored in
|
|
175
|
+
* .mj-folder.json files. Child folders can override parent defaults.
|
|
176
|
+
*/
|
|
38
177
|
export interface FolderConfig {
|
|
178
|
+
/** Default field values that apply to all entities in this folder and subfolders */
|
|
39
179
|
defaults: Record<string, any>;
|
|
40
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Load MemberJunction configuration from the filesystem
|
|
183
|
+
*
|
|
184
|
+
* Searches for mj.config.cjs starting from the current directory and walking up
|
|
185
|
+
* the directory tree. Uses cosmiconfig for flexible configuration loading.
|
|
186
|
+
*
|
|
187
|
+
* @returns MJConfig object if found, null if not found or invalid
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const config = loadMJConfig();
|
|
192
|
+
* if (config) {
|
|
193
|
+
* console.log(`Connecting to ${config.dbHost}:${config.dbPort || 1433}`);
|
|
194
|
+
* }
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
41
197
|
export declare function loadMJConfig(): MJConfig | null;
|
|
198
|
+
/**
|
|
199
|
+
* Load sync configuration from a directory
|
|
200
|
+
*
|
|
201
|
+
* Loads .mj-sync.json file from the specified directory. This file can contain
|
|
202
|
+
* either global sync configuration (no entity field) or entity-specific
|
|
203
|
+
* configuration (with entity field).
|
|
204
|
+
*
|
|
205
|
+
* @param dir - Directory path to load configuration from
|
|
206
|
+
* @returns Promise resolving to SyncConfig if found and valid, null otherwise
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* const syncConfig = await loadSyncConfig('/path/to/project');
|
|
211
|
+
* if (syncConfig?.push?.requireConfirmation) {
|
|
212
|
+
* // Show confirmation prompt
|
|
213
|
+
* }
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
42
216
|
export declare function loadSyncConfig(dir: string): Promise<SyncConfig | null>;
|
|
217
|
+
/**
|
|
218
|
+
* Load entity-specific configuration from a directory
|
|
219
|
+
*
|
|
220
|
+
* Loads .mj-sync.json file that contains an "entity" field, indicating this
|
|
221
|
+
* directory contains data for a specific entity type. Returns null if the
|
|
222
|
+
* file doesn't exist or doesn't contain an entity field.
|
|
223
|
+
*
|
|
224
|
+
* @param dir - Directory path to load configuration from
|
|
225
|
+
* @returns Promise resolving to EntityConfig if found and valid, null otherwise
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const entityConfig = await loadEntityConfig('./ai-prompts');
|
|
230
|
+
* if (entityConfig) {
|
|
231
|
+
* console.log(`Directory contains ${entityConfig.entity} records`);
|
|
232
|
+
* }
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
43
235
|
export declare function loadEntityConfig(dir: string): Promise<EntityConfig | null>;
|
|
236
|
+
/**
|
|
237
|
+
* Load folder-level configuration
|
|
238
|
+
*
|
|
239
|
+
* Loads .mj-folder.json file that contains default values to be applied to
|
|
240
|
+
* all entities in this folder and its subfolders. Used for cascading defaults
|
|
241
|
+
* in deep directory structures.
|
|
242
|
+
*
|
|
243
|
+
* @param dir - Directory path to load configuration from
|
|
244
|
+
* @returns Promise resolving to FolderConfig if found and valid, null otherwise
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* const folderConfig = await loadFolderConfig('./templates');
|
|
249
|
+
* if (folderConfig?.defaults) {
|
|
250
|
+
* // Apply folder defaults to records
|
|
251
|
+
* }
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
44
254
|
export declare function loadFolderConfig(dir: string): Promise<FolderConfig | null>;
|
package/dist/config.js
CHANGED
|
@@ -1,27 +1,61 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Configuration types and loaders for MetadataSync
|
|
4
|
+
* @module config
|
|
5
|
+
*
|
|
6
|
+
* This module defines configuration interfaces and provides utilities for loading
|
|
7
|
+
* various configuration files used by the MetadataSync tool. It supports:
|
|
8
|
+
* - MemberJunction database configuration (mj.config.cjs)
|
|
9
|
+
* - Sync configuration (.mj-sync.json)
|
|
10
|
+
* - Entity-specific configuration (.mj-sync.json with entity field)
|
|
11
|
+
* - Folder-level defaults (.mj-folder.json)
|
|
12
|
+
*/
|
|
2
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
15
|
};
|
|
5
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
17
|
exports.loadFolderConfig = exports.loadEntityConfig = exports.loadSyncConfig = exports.loadMJConfig = void 0;
|
|
7
|
-
const cosmiconfig_1 = require("cosmiconfig");
|
|
8
18
|
const path_1 = __importDefault(require("path"));
|
|
9
19
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
20
|
+
const config_manager_1 = require("./lib/config-manager");
|
|
21
|
+
/**
|
|
22
|
+
* Load MemberJunction configuration from the filesystem
|
|
23
|
+
*
|
|
24
|
+
* Searches for mj.config.cjs starting from the current directory and walking up
|
|
25
|
+
* the directory tree. Uses cosmiconfig for flexible configuration loading.
|
|
26
|
+
*
|
|
27
|
+
* @returns MJConfig object if found, null if not found or invalid
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const config = loadMJConfig();
|
|
32
|
+
* if (config) {
|
|
33
|
+
* console.log(`Connecting to ${config.dbHost}:${config.dbPort || 1433}`);
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
10
37
|
function loadMJConfig() {
|
|
11
|
-
|
|
12
|
-
const explorer = (0, cosmiconfig_1.cosmiconfigSync)('mj');
|
|
13
|
-
const result = explorer.search(process.cwd());
|
|
14
|
-
if (!result || !result.config) {
|
|
15
|
-
throw new Error('No mj.config.cjs found');
|
|
16
|
-
}
|
|
17
|
-
return result.config;
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
console.error('Error loading MJ config:', error);
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
38
|
+
return config_manager_1.configManager.loadMJConfig();
|
|
23
39
|
}
|
|
24
40
|
exports.loadMJConfig = loadMJConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Load sync configuration from a directory
|
|
43
|
+
*
|
|
44
|
+
* Loads .mj-sync.json file from the specified directory. This file can contain
|
|
45
|
+
* either global sync configuration (no entity field) or entity-specific
|
|
46
|
+
* configuration (with entity field).
|
|
47
|
+
*
|
|
48
|
+
* @param dir - Directory path to load configuration from
|
|
49
|
+
* @returns Promise resolving to SyncConfig if found and valid, null otherwise
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const syncConfig = await loadSyncConfig('/path/to/project');
|
|
54
|
+
* if (syncConfig?.push?.requireConfirmation) {
|
|
55
|
+
* // Show confirmation prompt
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
25
59
|
async function loadSyncConfig(dir) {
|
|
26
60
|
const configPath = path_1.default.join(dir, '.mj-sync.json');
|
|
27
61
|
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
@@ -36,6 +70,24 @@ async function loadSyncConfig(dir) {
|
|
|
36
70
|
return null;
|
|
37
71
|
}
|
|
38
72
|
exports.loadSyncConfig = loadSyncConfig;
|
|
73
|
+
/**
|
|
74
|
+
* Load entity-specific configuration from a directory
|
|
75
|
+
*
|
|
76
|
+
* Loads .mj-sync.json file that contains an "entity" field, indicating this
|
|
77
|
+
* directory contains data for a specific entity type. Returns null if the
|
|
78
|
+
* file doesn't exist or doesn't contain an entity field.
|
|
79
|
+
*
|
|
80
|
+
* @param dir - Directory path to load configuration from
|
|
81
|
+
* @returns Promise resolving to EntityConfig if found and valid, null otherwise
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const entityConfig = await loadEntityConfig('./ai-prompts');
|
|
86
|
+
* if (entityConfig) {
|
|
87
|
+
* console.log(`Directory contains ${entityConfig.entity} records`);
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
39
91
|
async function loadEntityConfig(dir) {
|
|
40
92
|
const configPath = path_1.default.join(dir, '.mj-sync.json');
|
|
41
93
|
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
@@ -52,6 +104,24 @@ async function loadEntityConfig(dir) {
|
|
|
52
104
|
return null;
|
|
53
105
|
}
|
|
54
106
|
exports.loadEntityConfig = loadEntityConfig;
|
|
107
|
+
/**
|
|
108
|
+
* Load folder-level configuration
|
|
109
|
+
*
|
|
110
|
+
* Loads .mj-folder.json file that contains default values to be applied to
|
|
111
|
+
* all entities in this folder and its subfolders. Used for cascading defaults
|
|
112
|
+
* in deep directory structures.
|
|
113
|
+
*
|
|
114
|
+
* @param dir - Directory path to load configuration from
|
|
115
|
+
* @returns Promise resolving to FolderConfig if found and valid, null otherwise
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* const folderConfig = await loadFolderConfig('./templates');
|
|
120
|
+
* if (folderConfig?.defaults) {
|
|
121
|
+
* // Apply folder defaults to records
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
55
125
|
async function loadFolderConfig(dir) {
|
|
56
126
|
const configPath = path_1.default.join(dir, '.mj-folder.json');
|
|
57
127
|
if (await fs_extra_1.default.pathExists(configPath)) {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;AAAA,6CAA8C;AAC9C,gDAAwB;AACxB,wDAA0B;AA+C1B,SAAgB,YAAY;IAC1B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAA,6BAAe,EAAC,IAAI,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAE9C,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAdD,oCAcC;AAEM,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEnD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAbD,wCAaC;AAEM,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEnD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC7C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAfD,4CAeC;AAEM,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAErD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAbD,4CAaC","sourcesContent":["import { cosmiconfigSync } from 'cosmiconfig';\nimport path from 'path';\nimport fs from 'fs-extra';\n\nexport interface MJConfig {\n dbHost: string;\n dbPort?: number;\n dbDatabase: string;\n dbUsername: string;\n dbPassword: string;\n dbTrustServerCertificate?: string;\n dbInstanceName?: string;\n mjCoreSchema?: string;\n [key: string]: any; // Allow other properties\n}\n\nexport interface SyncConfig {\n version: string;\n push?: {\n validateBeforePush?: boolean;\n requireConfirmation?: boolean;\n };\n watch?: {\n debounceMs?: number;\n ignorePatterns?: string[];\n };\n}\n\nexport interface RelatedEntityConfig {\n entity: string;\n foreignKey: string; // Field that links to parent (e.g., \"PromptID\")\n filter?: string; // Additional filter\n relatedEntities?: Record<string, RelatedEntityConfig>; // Nested related entities\n}\n\nexport interface EntityConfig {\n entity: string;\n filePattern?: string;\n defaults?: Record<string, any>;\n pull?: {\n filter?: string; // Default filter for pulling records\n relatedEntities?: Record<string, RelatedEntityConfig>;\n };\n}\n\nexport interface FolderConfig {\n defaults: Record<string, any>;\n}\n\nexport function loadMJConfig(): MJConfig | null {\n try {\n const explorer = cosmiconfigSync('mj');\n const result = explorer.search(process.cwd());\n \n if (!result || !result.config) {\n throw new Error('No mj.config.cjs found');\n }\n \n return result.config;\n } catch (error) {\n console.error('Error loading MJ config:', error);\n return null;\n }\n}\n\nexport async function loadSyncConfig(dir: string): Promise<SyncConfig | null> {\n const configPath = path.join(dir, '.mj-sync.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error('Error loading sync config:', error);\n return null;\n }\n }\n \n return null;\n}\n\nexport async function loadEntityConfig(dir: string): Promise<EntityConfig | null> {\n const configPath = path.join(dir, '.mj-sync.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n const config = await fs.readJson(configPath);\n if (config.entity) {\n return config;\n }\n } catch (error) {\n console.error('Error loading entity config:', error);\n }\n }\n \n return null;\n}\n\nexport async function loadFolderConfig(dir: string): Promise<FolderConfig | null> {\n const configPath = path.join(dir, '.mj-folder.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error('Error loading folder config:', error);\n return null;\n }\n }\n \n return null;\n}"]}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;AAGH,gDAAwB;AACxB,wDAA0B;AAC1B,yDAAqD;AAgLrD;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,YAAY;IAC1B,OAAO,8BAAa,CAAC,YAAY,EAAE,CAAC;AACtC,CAAC;AAFD,oCAEC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEnD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAbD,wCAaC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEnD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC7C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAfD,4CAeC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAErD,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAbD,4CAaC","sourcesContent":["/**\n * @fileoverview Configuration types and loaders for MetadataSync\n * @module config\n * \n * This module defines configuration interfaces and provides utilities for loading\n * various configuration files used by the MetadataSync tool. It supports:\n * - MemberJunction database configuration (mj.config.cjs)\n * - Sync configuration (.mj-sync.json)\n * - Entity-specific configuration (.mj-sync.json with entity field)\n * - Folder-level defaults (.mj-folder.json)\n */\n\nimport { cosmiconfigSync } from 'cosmiconfig';\nimport path from 'path';\nimport fs from 'fs-extra';\nimport { configManager } from './lib/config-manager';\n\n/**\n * MemberJunction database configuration\n * \n * Defines connection parameters and settings for connecting to the MemberJunction\n * database. Typically loaded from mj.config.cjs in the project root.\n */\nexport interface MJConfig {\n /** Database server hostname or IP address */\n dbHost: string;\n /** Database server port (defaults to 1433 for SQL Server) */\n dbPort?: number;\n /** Database name to connect to */\n dbDatabase: string;\n /** Database authentication username */\n dbUsername: string;\n /** Database authentication password */\n dbPassword: string;\n /** Whether to trust the server certificate (Y/N) */\n dbTrustServerCertificate?: string;\n /** Whether to encrypt the connection (Y/N, auto-detected for Azure SQL) */\n dbEncrypt?: string;\n /** SQL Server instance name (for named instances) */\n dbInstanceName?: string;\n /** Schema name for MemberJunction core tables (defaults to __mj) */\n mjCoreSchema?: string;\n /** Allow additional properties for extensibility */\n [key: string]: any;\n}\n\n/**\n * Global sync configuration\n * \n * Defines settings that apply to the entire sync process, including push/pull\n * behaviors and watch mode configuration. Stored in .mj-sync.json at the root.\n */\nexport interface SyncConfig {\n /** Version of the sync configuration format */\n version: string;\n /** Push command configuration */\n push?: {\n /** Whether to validate records before pushing to database */\n validateBeforePush?: boolean;\n /** Whether to require user confirmation before push */\n requireConfirmation?: boolean;\n };\n /** Watch command configuration */\n watch?: {\n /** Milliseconds to wait before processing file changes */\n debounceMs?: number;\n /** File patterns to ignore during watch */\n ignorePatterns?: string[];\n };\n}\n\n/**\n * Configuration for related entity synchronization\n * \n * Defines how to pull and push related entities that have foreign key relationships\n * with a parent entity. Supports nested relationships for deep object graphs.\n */\nexport interface RelatedEntityConfig {\n /** Name of the related entity to sync */\n entity: string;\n /** Field name that contains the foreign key reference to parent (e.g., \"PromptID\") */\n foreignKey: string;\n /** Optional SQL filter to apply when pulling related records */\n filter?: string;\n /** Fields to externalize to separate files for this related entity */\n externalizeFields?: string[] | {\n [fieldName: string]: {\n /** File extension to use (e.g., \".md\", \".txt\", \".html\") */\n extension?: string;\n }\n } | Array<{\n /** Field name to externalize */\n field: string;\n /** Pattern for the output file. Supports placeholders from the entity */\n pattern: string;\n }>;\n /** Fields to exclude from the pulled data for this related entity */\n excludeFields?: string[];\n /** Foreign key fields to convert to @lookup references for this related entity */\n lookupFields?: {\n [fieldName: string]: {\n entity: string;\n field: string;\n };\n };\n /** Nested related entities for deep object graphs */\n relatedEntities?: Record<string, RelatedEntityConfig>;\n}\n\n/**\n * Entity-specific configuration\n * \n * Defines settings for a specific entity type within a directory. Stored in\n * .mj-sync.json files that contain an \"entity\" field. Supports defaults,\n * file patterns, and related entity configuration.\n */\nexport interface EntityConfig {\n /** Name of the entity this directory contains */\n entity: string;\n /** Glob pattern for finding data files (defaults to \"*.json\") */\n filePattern?: string;\n /** Default field values applied to all records in this directory */\n defaults?: Record<string, any>;\n /** Pull command specific configuration */\n pull?: {\n /** Glob pattern for finding existing files to update (defaults to filePattern) */\n filePattern?: string;\n /** Whether to create new files for records not found locally */\n createNewFileIfNotFound?: boolean;\n /** Filename for new records when createNewFileIfNotFound is true */\n newFileName?: string;\n /** Whether to append multiple new records to a single file */\n appendRecordsToExistingFile?: boolean;\n /** Whether to update existing records found in local files */\n updateExistingRecords?: boolean;\n /** Fields to preserve during updates (never overwrite these) */\n preserveFields?: string[];\n /** Strategy for merging updates: \"overwrite\" | \"merge\" | \"skip\" */\n mergeStrategy?: 'overwrite' | 'merge' | 'skip';\n /** Create backup files before updating existing files */\n backupBeforeUpdate?: boolean;\n /** Directory name for backup files (defaults to \".backups\") */\n backupDirectory?: string;\n /** SQL filter to apply when pulling records from database */\n filter?: string;\n /** Configuration for pulling related entities */\n relatedEntities?: Record<string, RelatedEntityConfig>;\n /** Fields to externalize to separate files with optional configuration */\n externalizeFields?: string[] | {\n [fieldName: string]: {\n /** File extension to use (e.g., \".md\", \".txt\", \".html\") */\n extension?: string;\n }\n } | Array<{\n /** Field name to externalize */\n field: string;\n /** Pattern for the output file. Supports placeholders:\n * - {Name}: Entity's name field value\n * - {ID}: Entity's ID\n * - {FieldName}: The field being externalized\n * - Any other {FieldName} from the entity\n * Example: \"@file:templates/{Name}.template.md\"\n */\n pattern: string;\n }>;\n /** Fields to exclude from the pulled data (e.g., [\"TemplateID\"]) */\n excludeFields?: string[];\n /** Foreign key fields to convert to @lookup references */\n lookupFields?: {\n /** Field name in this entity (e.g., \"CategoryID\") */\n [fieldName: string]: {\n /** Target entity name (e.g., \"AI Prompt Categories\") */\n entity: string;\n /** Field in target entity to use for lookup (e.g., \"Name\") */\n field: string;\n };\n };\n };\n}\n\n/**\n * Folder-level configuration\n * \n * Defines default values that cascade down to all subdirectories. Stored in\n * .mj-folder.json files. Child folders can override parent defaults.\n */\nexport interface FolderConfig {\n /** Default field values that apply to all entities in this folder and subfolders */\n defaults: Record<string, any>;\n}\n\n/**\n * Load MemberJunction configuration from the filesystem\n * \n * Searches for mj.config.cjs starting from the current directory and walking up\n * the directory tree. Uses cosmiconfig for flexible configuration loading.\n * \n * @returns MJConfig object if found, null if not found or invalid\n * \n * @example\n * ```typescript\n * const config = loadMJConfig();\n * if (config) {\n * console.log(`Connecting to ${config.dbHost}:${config.dbPort || 1433}`);\n * }\n * ```\n */\nexport function loadMJConfig(): MJConfig | null {\n return configManager.loadMJConfig();\n}\n\n/**\n * Load sync configuration from a directory\n * \n * Loads .mj-sync.json file from the specified directory. This file can contain\n * either global sync configuration (no entity field) or entity-specific\n * configuration (with entity field).\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to SyncConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const syncConfig = await loadSyncConfig('/path/to/project');\n * if (syncConfig?.push?.requireConfirmation) {\n * // Show confirmation prompt\n * }\n * ```\n */\nexport async function loadSyncConfig(dir: string): Promise<SyncConfig | null> {\n const configPath = path.join(dir, '.mj-sync.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error('Error loading sync config:', error);\n return null;\n }\n }\n \n return null;\n}\n\n/**\n * Load entity-specific configuration from a directory\n * \n * Loads .mj-sync.json file that contains an \"entity\" field, indicating this\n * directory contains data for a specific entity type. Returns null if the\n * file doesn't exist or doesn't contain an entity field.\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to EntityConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const entityConfig = await loadEntityConfig('./ai-prompts');\n * if (entityConfig) {\n * console.log(`Directory contains ${entityConfig.entity} records`);\n * }\n * ```\n */\nexport async function loadEntityConfig(dir: string): Promise<EntityConfig | null> {\n const configPath = path.join(dir, '.mj-sync.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n const config = await fs.readJson(configPath);\n if (config.entity) {\n return config;\n }\n } catch (error) {\n console.error('Error loading entity config:', error);\n }\n }\n \n return null;\n}\n\n/**\n * Load folder-level configuration\n * \n * Loads .mj-folder.json file that contains default values to be applied to\n * all entities in this folder and its subfolders. Used for cascading defaults\n * in deep directory structures.\n * \n * @param dir - Directory path to load configuration from\n * @returns Promise resolving to FolderConfig if found and valid, null otherwise\n * \n * @example\n * ```typescript\n * const folderConfig = await loadFolderConfig('./templates');\n * if (folderConfig?.defaults) {\n * // Apply folder defaults to records\n * }\n * ```\n */\nexport async function loadFolderConfig(dir: string): Promise<FolderConfig | null> {\n const configPath = path.join(dir, '.mj-folder.json');\n \n if (await fs.pathExists(configPath)) {\n try {\n return await fs.readJson(configPath);\n } catch (error) {\n console.error('Error loading folder config:', error);\n return null;\n }\n }\n \n return null;\n}"]}
|
package/dist/hooks/init.js
CHANGED
|
@@ -27,9 +27,17 @@ const core_entities_server_1 = require("@memberjunction/core-entities-server");
|
|
|
27
27
|
const dotenv = __importStar(require("dotenv"));
|
|
28
28
|
const path = __importStar(require("path"));
|
|
29
29
|
const provider_utils_1 = require("../lib/provider-utils");
|
|
30
|
+
const config_manager_1 = require("../lib/config-manager");
|
|
30
31
|
const hook = async function () {
|
|
31
|
-
//
|
|
32
|
+
// Capture the original working directory FIRST before any changes
|
|
33
|
+
config_manager_1.configManager.getOriginalCwd();
|
|
34
|
+
// Load .env from the repository root first
|
|
32
35
|
dotenv.config({ path: path.join(__dirname, '../../../../.env') });
|
|
36
|
+
// Then load local .env from package directory to override
|
|
37
|
+
dotenv.config({
|
|
38
|
+
path: path.join(__dirname, '../../.env'),
|
|
39
|
+
override: true // This ensures local values override already loaded ones
|
|
40
|
+
});
|
|
33
41
|
// Load core entities server subclasses
|
|
34
42
|
(0, core_entities_server_1.LoadCoreEntitiesServerSubClasses)();
|
|
35
43
|
// Register cleanup handlers
|
package/dist/hooks/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/hooks/init.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AACA,+EAAwF;AACxF,+CAAiC;AACjC,2CAA6B;AAC7B,0DAAwD;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/hooks/init.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AACA,+EAAwF;AACxF,+CAAiC;AACjC,2CAA6B;AAC7B,0DAAwD;AACxD,0DAAsD;AAEtD,MAAM,IAAI,GAAiB,KAAK;IAC9B,kEAAkE;IAClE,8BAAa,CAAC,cAAc,EAAE,CAAC;IAE/B,2CAA2C;IAC3C,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;IAElE,0DAA0D;IAC1D,MAAM,CAAC,MAAM,CAAC;QACZ,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC;QACxC,QAAQ,EAAE,IAAI,CAAE,yDAAyD;KAC1E,CAAC,CAAC;IAEH,uCAAuC;IACvC,IAAA,uDAAgC,GAAE,CAAC;IAEnC,4BAA4B;IAC5B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,IAAA,gCAAe,GAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YAC3B,+BAA+B;QACjC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,IAAA,gCAAe,GAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,IAAA,gCAAe,GAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,kBAAe,IAAI,CAAC","sourcesContent":["import { Hook } from '@oclif/core';\nimport { LoadCoreEntitiesServerSubClasses } from '@memberjunction/core-entities-server';\nimport * as dotenv from 'dotenv';\nimport * as path from 'path';\nimport { cleanupProvider } from '../lib/provider-utils';\nimport { configManager } from '../lib/config-manager';\n\nconst hook: Hook<'init'> = async function () {\n // Capture the original working directory FIRST before any changes\n configManager.getOriginalCwd();\n \n // Load .env from the repository root first\n dotenv.config({ path: path.join(__dirname, '../../../../.env') });\n \n // Then load local .env from package directory to override\n dotenv.config({ \n path: path.join(__dirname, '../../.env'),\n override: true // This ensures local values override already loaded ones\n });\n \n // Load core entities server subclasses\n LoadCoreEntitiesServerSubClasses();\n \n // Register cleanup handlers\n process.on('exit', () => {\n cleanupProvider().catch(() => {\n // Ignore errors during cleanup\n });\n });\n \n process.on('SIGINT', async () => {\n await cleanupProvider();\n process.exit(0);\n });\n \n process.on('SIGTERM', async () => {\n await cleanupProvider();\n process.exit(0);\n });\n};\n\nexport default hook;"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared configuration manager for MetadataSync commands
|
|
3
|
+
* @module lib/config-manager
|
|
4
|
+
*
|
|
5
|
+
* This module provides a centralized configuration management system that handles
|
|
6
|
+
* loading MJ config from the original working directory, regardless of any
|
|
7
|
+
* directory changes made during command execution.
|
|
8
|
+
*/
|
|
9
|
+
import { MJConfig } from '../config';
|
|
10
|
+
/**
|
|
11
|
+
* Configuration manager singleton for handling MJ configuration
|
|
12
|
+
*
|
|
13
|
+
* Stores the original working directory and MJ configuration to ensure
|
|
14
|
+
* consistent access across all commands, even when the current working
|
|
15
|
+
* directory changes during execution.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ConfigManager {
|
|
18
|
+
private static instance;
|
|
19
|
+
private originalCwd;
|
|
20
|
+
private mjConfig;
|
|
21
|
+
private configLoaded;
|
|
22
|
+
private constructor();
|
|
23
|
+
/**
|
|
24
|
+
* Get the singleton instance of ConfigManager
|
|
25
|
+
*/
|
|
26
|
+
static getInstance(): ConfigManager;
|
|
27
|
+
/**
|
|
28
|
+
* Get the original working directory from when the process started
|
|
29
|
+
*
|
|
30
|
+
* @returns The original working directory path
|
|
31
|
+
*/
|
|
32
|
+
getOriginalCwd(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Set the original working directory (for testing or special cases)
|
|
35
|
+
*
|
|
36
|
+
* @param cwd - The working directory to use as original
|
|
37
|
+
*/
|
|
38
|
+
setOriginalCwd(cwd: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Load MemberJunction configuration
|
|
41
|
+
*
|
|
42
|
+
* Searches for mj.config.cjs starting from the original working directory
|
|
43
|
+
* and walking up the directory tree. Caches the result for subsequent calls.
|
|
44
|
+
*
|
|
45
|
+
* @param forceReload - Force reload the configuration even if cached
|
|
46
|
+
* @returns MJConfig object if found, null if not found or invalid
|
|
47
|
+
*/
|
|
48
|
+
loadMJConfig(forceReload?: boolean): MJConfig | null;
|
|
49
|
+
/**
|
|
50
|
+
* Get the cached MJ configuration
|
|
51
|
+
*
|
|
52
|
+
* @returns The cached MJConfig or null if not loaded
|
|
53
|
+
*/
|
|
54
|
+
getMJConfig(): MJConfig | null;
|
|
55
|
+
}
|
|
56
|
+
export declare const configManager: ConfigManager;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Shared configuration manager for MetadataSync commands
|
|
4
|
+
* @module lib/config-manager
|
|
5
|
+
*
|
|
6
|
+
* This module provides a centralized configuration management system that handles
|
|
7
|
+
* loading MJ config from the original working directory, regardless of any
|
|
8
|
+
* directory changes made during command execution.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.configManager = exports.ConfigManager = void 0;
|
|
12
|
+
const cosmiconfig_1 = require("cosmiconfig");
|
|
13
|
+
/**
|
|
14
|
+
* Configuration manager singleton for handling MJ configuration
|
|
15
|
+
*
|
|
16
|
+
* Stores the original working directory and MJ configuration to ensure
|
|
17
|
+
* consistent access across all commands, even when the current working
|
|
18
|
+
* directory changes during execution.
|
|
19
|
+
*/
|
|
20
|
+
class ConfigManager {
|
|
21
|
+
static instance;
|
|
22
|
+
originalCwd = null;
|
|
23
|
+
mjConfig = null;
|
|
24
|
+
configLoaded = false;
|
|
25
|
+
constructor() {
|
|
26
|
+
// Original cwd will be set on first access
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the singleton instance of ConfigManager
|
|
30
|
+
*/
|
|
31
|
+
static getInstance() {
|
|
32
|
+
if (!ConfigManager.instance) {
|
|
33
|
+
ConfigManager.instance = new ConfigManager();
|
|
34
|
+
}
|
|
35
|
+
return ConfigManager.instance;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get the original working directory from when the process started
|
|
39
|
+
*
|
|
40
|
+
* @returns The original working directory path
|
|
41
|
+
*/
|
|
42
|
+
getOriginalCwd() {
|
|
43
|
+
if (!this.originalCwd) {
|
|
44
|
+
// Capture on first access
|
|
45
|
+
this.originalCwd = process.cwd();
|
|
46
|
+
}
|
|
47
|
+
return this.originalCwd;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Set the original working directory (for testing or special cases)
|
|
51
|
+
*
|
|
52
|
+
* @param cwd - The working directory to use as original
|
|
53
|
+
*/
|
|
54
|
+
setOriginalCwd(cwd) {
|
|
55
|
+
this.originalCwd = cwd;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Load MemberJunction configuration
|
|
59
|
+
*
|
|
60
|
+
* Searches for mj.config.cjs starting from the original working directory
|
|
61
|
+
* and walking up the directory tree. Caches the result for subsequent calls.
|
|
62
|
+
*
|
|
63
|
+
* @param forceReload - Force reload the configuration even if cached
|
|
64
|
+
* @returns MJConfig object if found, null if not found or invalid
|
|
65
|
+
*/
|
|
66
|
+
loadMJConfig(forceReload = false) {
|
|
67
|
+
if (this.configLoaded && !forceReload) {
|
|
68
|
+
return this.mjConfig;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const explorer = (0, cosmiconfig_1.cosmiconfigSync)('mj');
|
|
72
|
+
// Always search from the original working directory
|
|
73
|
+
const searchPath = this.getOriginalCwd();
|
|
74
|
+
const result = explorer.search(searchPath);
|
|
75
|
+
if (!result || !result.config) {
|
|
76
|
+
throw new Error('No mj.config.cjs found');
|
|
77
|
+
}
|
|
78
|
+
this.mjConfig = result.config;
|
|
79
|
+
this.configLoaded = true;
|
|
80
|
+
return this.mjConfig;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.error('Error loading MJ config:', error);
|
|
84
|
+
this.mjConfig = null;
|
|
85
|
+
this.configLoaded = true;
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get the cached MJ configuration
|
|
91
|
+
*
|
|
92
|
+
* @returns The cached MJConfig or null if not loaded
|
|
93
|
+
*/
|
|
94
|
+
getMJConfig() {
|
|
95
|
+
if (!this.configLoaded) {
|
|
96
|
+
return this.loadMJConfig();
|
|
97
|
+
}
|
|
98
|
+
return this.mjConfig;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.ConfigManager = ConfigManager;
|
|
102
|
+
// Export singleton instance for convenience
|
|
103
|
+
exports.configManager = ConfigManager.getInstance();
|
|
104
|
+
//# sourceMappingURL=config-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/lib/config-manager.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,6CAA8C;AAG9C;;;;;;GAMG;AACH,MAAa,aAAa;IAChB,MAAM,CAAC,QAAQ,CAAgB;IAC/B,WAAW,GAAkB,IAAI,CAAC;IAClC,QAAQ,GAAoB,IAAI,CAAC;IACjC,YAAY,GAAG,KAAK,CAAC;IAE7B;QACE,2CAA2C;IAC7C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,WAAW;QAChB,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YAC5B,aAAa,CAAC,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QAC/C,CAAC;QACD,OAAO,aAAa,CAAC,QAAQ,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACH,cAAc;QACZ,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,0BAA0B;YAC1B,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QACnC,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,cAAc,CAAC,GAAW;QACxB,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;IACzB,CAAC;IAED;;;;;;;;OAQG;IACH,YAAY,CAAC,WAAW,GAAG,KAAK;QAC9B,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,WAAW,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC,QAAQ,CAAC;QACvB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAA,6BAAe,EAAC,IAAI,CAAC,CAAC;YACvC,oDAAoD;YACpD,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAE3C,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC5C,CAAC;YAED,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;YAC9B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,OAAO,IAAI,CAAC,QAAQ,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;YACjD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF;AAxFD,sCAwFC;AAED,4CAA4C;AAC/B,QAAA,aAAa,GAAG,aAAa,CAAC,WAAW,EAAE,CAAC","sourcesContent":["/**\n * @fileoverview Shared configuration manager for MetadataSync commands\n * @module lib/config-manager\n * \n * This module provides a centralized configuration management system that handles\n * loading MJ config from the original working directory, regardless of any\n * directory changes made during command execution.\n */\n\nimport { cosmiconfigSync } from 'cosmiconfig';\nimport { MJConfig } from '../config';\n\n/**\n * Configuration manager singleton for handling MJ configuration\n * \n * Stores the original working directory and MJ configuration to ensure\n * consistent access across all commands, even when the current working\n * directory changes during execution.\n */\nexport class ConfigManager {\n private static instance: ConfigManager;\n private originalCwd: string | null = null;\n private mjConfig: MJConfig | null = null;\n private configLoaded = false;\n\n private constructor() {\n // Original cwd will be set on first access\n }\n\n /**\n * Get the singleton instance of ConfigManager\n */\n static getInstance(): ConfigManager {\n if (!ConfigManager.instance) {\n ConfigManager.instance = new ConfigManager();\n }\n return ConfigManager.instance;\n }\n\n /**\n * Get the original working directory from when the process started\n * \n * @returns The original working directory path\n */\n getOriginalCwd(): string {\n if (!this.originalCwd) {\n // Capture on first access\n this.originalCwd = process.cwd();\n }\n return this.originalCwd;\n }\n \n /**\n * Set the original working directory (for testing or special cases)\n * \n * @param cwd - The working directory to use as original\n */\n setOriginalCwd(cwd: string): void {\n this.originalCwd = cwd;\n }\n\n /**\n * Load MemberJunction configuration\n * \n * Searches for mj.config.cjs starting from the original working directory\n * and walking up the directory tree. Caches the result for subsequent calls.\n * \n * @param forceReload - Force reload the configuration even if cached\n * @returns MJConfig object if found, null if not found or invalid\n */\n loadMJConfig(forceReload = false): MJConfig | null {\n if (this.configLoaded && !forceReload) {\n return this.mjConfig;\n }\n\n try {\n const explorer = cosmiconfigSync('mj');\n // Always search from the original working directory\n const searchPath = this.getOriginalCwd();\n const result = explorer.search(searchPath);\n \n if (!result || !result.config) {\n throw new Error('No mj.config.cjs found');\n }\n \n this.mjConfig = result.config;\n this.configLoaded = true;\n return this.mjConfig;\n } catch (error) {\n console.error('Error loading MJ config:', error);\n this.mjConfig = null;\n this.configLoaded = true;\n return null;\n }\n }\n\n /**\n * Get the cached MJ configuration\n * \n * @returns The cached MJConfig or null if not loaded\n */\n getMJConfig(): MJConfig | null {\n if (!this.configLoaded) {\n return this.loadMJConfig();\n }\n return this.mjConfig;\n }\n}\n\n// Export singleton instance for convenience\nexport const configManager = ConfigManager.getInstance();"]}
|