@memberjunction/metadata-sync 2.46.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 +552 -0
- package/bin/debug.js +7 -0
- package/bin/run +17 -0
- package/bin/run.js +6 -0
- package/dist/commands/init/index.d.ts +7 -0
- package/dist/commands/init/index.js +130 -0
- package/dist/commands/init/index.js.map +1 -0
- package/dist/commands/pull/index.d.ts +18 -0
- package/dist/commands/pull/index.js +313 -0
- package/dist/commands/pull/index.js.map +1 -0
- package/dist/commands/push/index.d.ts +15 -0
- package/dist/commands/push/index.js +286 -0
- package/dist/commands/push/index.js.map +1 -0
- package/dist/commands/status/index.d.ts +10 -0
- package/dist/commands/status/index.js +124 -0
- package/dist/commands/status/index.js.map +1 -0
- package/dist/commands/watch/index.d.ts +15 -0
- package/dist/commands/watch/index.js +210 -0
- package/dist/commands/watch/index.js.map +1 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.js +69 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/init.d.ts +3 -0
- package/dist/hooks/init.js +51 -0
- package/dist/hooks/init.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/provider-utils.d.ts +13 -0
- package/dist/lib/provider-utils.js +117 -0
- package/dist/lib/provider-utils.js.map +1 -0
- package/dist/lib/sync-engine.d.ts +49 -0
- package/dist/lib/sync-engine.js +254 -0
- package/dist/lib/sync-engine.js.map +1 -0
- package/oclif.manifest.json +196 -0
- package/package.json +85 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SyncEngine = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
const core_1 = require("@memberjunction/core");
|
|
12
|
+
class SyncEngine {
|
|
13
|
+
metadata;
|
|
14
|
+
contextUser;
|
|
15
|
+
constructor(contextUser) {
|
|
16
|
+
this.metadata = new core_1.Metadata();
|
|
17
|
+
this.contextUser = contextUser;
|
|
18
|
+
}
|
|
19
|
+
async initialize() {
|
|
20
|
+
// Initialize metadata
|
|
21
|
+
await this.metadata.Refresh();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Process special references in field values
|
|
25
|
+
*/
|
|
26
|
+
async processFieldValue(value, baseDir, parentRecord, rootRecord) {
|
|
27
|
+
if (typeof value !== 'string') {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
// Check for @parent: reference
|
|
31
|
+
if (value.startsWith('@parent:')) {
|
|
32
|
+
if (!parentRecord) {
|
|
33
|
+
throw new Error(`@parent reference used but no parent record available: ${value}`);
|
|
34
|
+
}
|
|
35
|
+
const fieldName = value.substring(8);
|
|
36
|
+
return parentRecord.Get(fieldName);
|
|
37
|
+
}
|
|
38
|
+
// Check for @root: reference
|
|
39
|
+
if (value.startsWith('@root:')) {
|
|
40
|
+
if (!rootRecord) {
|
|
41
|
+
throw new Error(`@root reference used but no root record available: ${value}`);
|
|
42
|
+
}
|
|
43
|
+
const fieldName = value.substring(6);
|
|
44
|
+
return rootRecord.Get(fieldName);
|
|
45
|
+
}
|
|
46
|
+
// Check for @file: reference
|
|
47
|
+
if (value.startsWith('@file:')) {
|
|
48
|
+
const filePath = value.substring(6);
|
|
49
|
+
const fullPath = path_1.default.resolve(baseDir, filePath);
|
|
50
|
+
if (await fs_extra_1.default.pathExists(fullPath)) {
|
|
51
|
+
return await fs_extra_1.default.readFile(fullPath, 'utf-8');
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
throw new Error(`File not found: ${fullPath}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Check for @url: reference
|
|
58
|
+
if (value.startsWith('@url:')) {
|
|
59
|
+
const url = value.substring(5);
|
|
60
|
+
try {
|
|
61
|
+
const response = await axios_1.default.get(url);
|
|
62
|
+
return response.data;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
throw new Error(`Failed to fetch URL: ${url} - ${error}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Check for @lookup: reference
|
|
69
|
+
if (value.startsWith('@lookup:')) {
|
|
70
|
+
const lookupStr = value.substring(8);
|
|
71
|
+
// Parse lookup with optional create syntax
|
|
72
|
+
// Format: EntityName.FieldName=Value?create&OtherField=Value
|
|
73
|
+
const entityMatch = lookupStr.match(/^([^.]+)\./);
|
|
74
|
+
if (!entityMatch) {
|
|
75
|
+
throw new Error(`Invalid lookup format: ${value}`);
|
|
76
|
+
}
|
|
77
|
+
const entityName = entityMatch[1];
|
|
78
|
+
const remaining = lookupStr.substring(entityName.length + 1);
|
|
79
|
+
// Check if this has ?create syntax
|
|
80
|
+
const hasCreate = remaining.includes('?create');
|
|
81
|
+
const lookupPart = hasCreate ? remaining.split('?')[0] : remaining;
|
|
82
|
+
// Parse the main lookup field
|
|
83
|
+
const fieldMatch = lookupPart.match(/^(.+?)=(.+)$/);
|
|
84
|
+
if (!fieldMatch) {
|
|
85
|
+
throw new Error(`Invalid lookup format: ${value}`);
|
|
86
|
+
}
|
|
87
|
+
const [, fieldName, fieldValue] = fieldMatch;
|
|
88
|
+
// Parse additional fields for creation if ?create is present
|
|
89
|
+
let createFields = {};
|
|
90
|
+
if (hasCreate && remaining.includes('?create&')) {
|
|
91
|
+
const createPart = remaining.split('?create&')[1];
|
|
92
|
+
const pairs = createPart.split('&');
|
|
93
|
+
for (const pair of pairs) {
|
|
94
|
+
const [key, val] = pair.split('=');
|
|
95
|
+
if (key && val) {
|
|
96
|
+
createFields[key] = decodeURIComponent(val);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return await this.resolveLookup(entityName, fieldName, fieldValue, hasCreate, createFields);
|
|
101
|
+
}
|
|
102
|
+
// Check for @env: reference
|
|
103
|
+
if (value.startsWith('@env:')) {
|
|
104
|
+
const envVar = value.substring(5);
|
|
105
|
+
const envValue = process.env[envVar];
|
|
106
|
+
if (envValue === undefined) {
|
|
107
|
+
throw new Error(`Environment variable not found: ${envVar}`);
|
|
108
|
+
}
|
|
109
|
+
return envValue;
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve a lookup reference to an ID, optionally creating the record if it doesn't exist
|
|
115
|
+
*/
|
|
116
|
+
async resolveLookup(entityName, fieldName, fieldValue, autoCreate = false, createFields = {}) {
|
|
117
|
+
// Debug logging handled by caller if needed
|
|
118
|
+
const rv = new core_1.RunView();
|
|
119
|
+
const entityInfo = this.metadata.EntityByName(entityName);
|
|
120
|
+
if (!entityInfo) {
|
|
121
|
+
throw new Error(`Entity not found: ${entityName}`);
|
|
122
|
+
}
|
|
123
|
+
const field = entityInfo.Fields.find(f => f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());
|
|
124
|
+
const quotes = field?.NeedsQuotes ? "'" : '';
|
|
125
|
+
const result = await rv.RunView({
|
|
126
|
+
EntityName: entityName,
|
|
127
|
+
ExtraFilter: `${fieldName} = ${quotes}${fieldValue.replace(/'/g, "''")}${quotes}`,
|
|
128
|
+
MaxRows: 1
|
|
129
|
+
}, this.contextUser);
|
|
130
|
+
if (result.Success && result.Results.length > 0) {
|
|
131
|
+
if (entityInfo.PrimaryKeys.length > 0) {
|
|
132
|
+
const pkeyField = entityInfo.PrimaryKeys[0].Name;
|
|
133
|
+
const id = result.Results[0][pkeyField];
|
|
134
|
+
return id;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// If not found and auto-create is enabled, create the record
|
|
138
|
+
if (autoCreate) {
|
|
139
|
+
const newEntity = await this.metadata.GetEntityObject(entityName, this.contextUser);
|
|
140
|
+
if (!newEntity) {
|
|
141
|
+
throw new Error(`Failed to create entity object for: ${entityName}`);
|
|
142
|
+
}
|
|
143
|
+
newEntity.NewRecord();
|
|
144
|
+
// Set the lookup field
|
|
145
|
+
if (fieldName in newEntity) {
|
|
146
|
+
newEntity[fieldName] = fieldValue;
|
|
147
|
+
}
|
|
148
|
+
// Set any additional fields provided
|
|
149
|
+
for (const [key, value] of Object.entries(createFields)) {
|
|
150
|
+
if (key in newEntity) {
|
|
151
|
+
newEntity[key] = value;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Save the new record
|
|
155
|
+
const saved = await newEntity.Save();
|
|
156
|
+
if (!saved) {
|
|
157
|
+
const errors = newEntity.LatestResult?.Errors?.join(', ') || 'Unknown error';
|
|
158
|
+
throw new Error(`Failed to auto-create ${entityName}: ${errors}`);
|
|
159
|
+
}
|
|
160
|
+
// Return the new ID
|
|
161
|
+
if (entityInfo.PrimaryKeys.length > 0) {
|
|
162
|
+
const pkeyField = entityInfo.PrimaryKeys[0].Name;
|
|
163
|
+
const newId = newEntity.Get(pkeyField);
|
|
164
|
+
return newId;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw new Error(`Lookup failed: No record found in '${entityName}' where ${fieldName}='${fieldValue}'`);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Build cascading defaults for a file path and process field values
|
|
171
|
+
*/
|
|
172
|
+
async buildDefaults(filePath, entityConfig) {
|
|
173
|
+
const parts = path_1.default.dirname(filePath).split(path_1.default.sep);
|
|
174
|
+
let defaults = { ...entityConfig.defaults };
|
|
175
|
+
// Walk up the directory tree building defaults
|
|
176
|
+
let currentPath = '';
|
|
177
|
+
for (const part of parts) {
|
|
178
|
+
currentPath = path_1.default.join(currentPath, part);
|
|
179
|
+
const folderConfig = await this.loadFolderConfig(currentPath);
|
|
180
|
+
if (folderConfig?.defaults) {
|
|
181
|
+
defaults = { ...defaults, ...folderConfig.defaults };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Process all default values (lookups, file references, etc.)
|
|
185
|
+
const processedDefaults = {};
|
|
186
|
+
const baseDir = path_1.default.dirname(filePath);
|
|
187
|
+
for (const [field, value] of Object.entries(defaults)) {
|
|
188
|
+
try {
|
|
189
|
+
processedDefaults[field] = await this.processFieldValue(value, baseDir, null, null);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
throw new Error(`Failed to process default for field '${field}': ${error}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return processedDefaults;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Load folder configuration
|
|
199
|
+
*/
|
|
200
|
+
async loadFolderConfig(dir) {
|
|
201
|
+
const configPath = path_1.default.join(dir, '.mj-folder.json');
|
|
202
|
+
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
203
|
+
try {
|
|
204
|
+
return await fs_extra_1.default.readJson(configPath);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
console.error(`Error loading folder config at ${configPath}:`, error);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Calculate checksum for data
|
|
215
|
+
*/
|
|
216
|
+
calculateChecksum(data) {
|
|
217
|
+
const hash = crypto_1.default.createHash('sha256');
|
|
218
|
+
hash.update(JSON.stringify(data, null, 2));
|
|
219
|
+
return hash.digest('hex');
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get entity info by name
|
|
223
|
+
*/
|
|
224
|
+
getEntityInfo(entityName) {
|
|
225
|
+
return this.metadata.EntityByName(entityName);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Create a new entity object
|
|
229
|
+
*/
|
|
230
|
+
async createEntityObject(entityName) {
|
|
231
|
+
const entity = await this.metadata.GetEntityObject(entityName, this.contextUser);
|
|
232
|
+
if (!entity) {
|
|
233
|
+
throw new Error(`Failed to create entity object for: ${entityName}`);
|
|
234
|
+
}
|
|
235
|
+
return entity;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Load an entity by primary key
|
|
239
|
+
*/
|
|
240
|
+
async loadEntity(entityName, primaryKey) {
|
|
241
|
+
const entity = await this.createEntityObject(entityName);
|
|
242
|
+
const entityInfo = this.getEntityInfo(entityName);
|
|
243
|
+
if (!entityInfo) {
|
|
244
|
+
throw new Error(`Entity not found: ${entityName}`);
|
|
245
|
+
}
|
|
246
|
+
// Handle composite keys
|
|
247
|
+
const compositeKey = new core_1.CompositeKey();
|
|
248
|
+
compositeKey.LoadFromSimpleObject(primaryKey);
|
|
249
|
+
const loaded = await entity.InnerLoad(compositeKey);
|
|
250
|
+
return loaded ? entity : null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
exports.SyncEngine = SyncEngine;
|
|
254
|
+
//# sourceMappingURL=sync-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../../src/lib/sync-engine.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport fs from 'fs-extra';\nimport crypto from 'crypto';\nimport axios from 'axios';\nimport { EntityInfo, Metadata, RunView, BaseEntity, CompositeKey, UserInfo } from '@memberjunction/core';\nimport { EntityConfig, FolderConfig } from '../config';\n\nexport interface RecordData {\n primaryKey?: Record<string, any>;\n fields: Record<string, any>;\n relatedEntities?: Record<string, RecordData[]>;\n sync?: {\n lastModified: string;\n checksum: string;\n };\n}\n\nexport class SyncEngine {\n private metadata: Metadata;\n private contextUser: UserInfo;\n\n constructor(contextUser: UserInfo) {\n this.metadata = new Metadata();\n this.contextUser = contextUser;\n }\n \n async initialize(): Promise<void> {\n // Initialize metadata\n await this.metadata.Refresh();\n }\n \n /**\n * Process special references in field values\n */\n async processFieldValue(value: any, baseDir: string, parentRecord?: BaseEntity | null, rootRecord?: BaseEntity | null): Promise<any> {\n if (typeof value !== 'string') {\n return value;\n }\n \n // Check for @parent: reference\n if (value.startsWith('@parent:')) {\n if (!parentRecord) {\n throw new Error(`@parent reference used but no parent record available: ${value}`);\n }\n const fieldName = value.substring(8);\n return parentRecord.Get(fieldName);\n }\n \n // Check for @root: reference\n if (value.startsWith('@root:')) {\n if (!rootRecord) {\n throw new Error(`@root reference used but no root record available: ${value}`);\n }\n const fieldName = value.substring(6);\n return rootRecord.Get(fieldName);\n }\n \n // Check for @file: reference\n if (value.startsWith('@file:')) {\n const filePath = value.substring(6);\n const fullPath = path.resolve(baseDir, filePath);\n \n if (await fs.pathExists(fullPath)) {\n return await fs.readFile(fullPath, 'utf-8');\n } else {\n throw new Error(`File not found: ${fullPath}`);\n }\n }\n \n // Check for @url: reference\n if (value.startsWith('@url:')) {\n const url = value.substring(5);\n \n try {\n const response = await axios.get(url);\n return response.data;\n } catch (error) {\n throw new Error(`Failed to fetch URL: ${url} - ${error}`);\n }\n }\n \n // Check for @lookup: reference\n if (value.startsWith('@lookup:')) {\n const lookupStr = value.substring(8);\n \n // Parse lookup with optional create syntax\n // Format: EntityName.FieldName=Value?create&OtherField=Value\n const entityMatch = lookupStr.match(/^([^.]+)\\./);\n if (!entityMatch) {\n throw new Error(`Invalid lookup format: ${value}`);\n }\n \n const entityName = entityMatch[1];\n const remaining = lookupStr.substring(entityName.length + 1);\n \n // Check if this has ?create syntax\n const hasCreate = remaining.includes('?create');\n const lookupPart = hasCreate ? remaining.split('?')[0] : remaining;\n \n // Parse the main lookup field\n const fieldMatch = lookupPart.match(/^(.+?)=(.+)$/);\n if (!fieldMatch) {\n throw new Error(`Invalid lookup format: ${value}`);\n }\n \n const [, fieldName, fieldValue] = fieldMatch;\n \n // Parse additional fields for creation if ?create is present\n let createFields: Record<string, any> = {};\n if (hasCreate && remaining.includes('?create&')) {\n const createPart = remaining.split('?create&')[1];\n const pairs = createPart.split('&');\n for (const pair of pairs) {\n const [key, val] = pair.split('=');\n if (key && val) {\n createFields[key] = decodeURIComponent(val);\n }\n }\n }\n \n return await this.resolveLookup(entityName, fieldName, fieldValue, hasCreate, createFields);\n }\n \n // Check for @env: reference\n if (value.startsWith('@env:')) {\n const envVar = value.substring(5);\n const envValue = process.env[envVar];\n \n if (envValue === undefined) {\n throw new Error(`Environment variable not found: ${envVar}`);\n }\n \n return envValue;\n }\n \n return value;\n }\n \n /**\n * Resolve a lookup reference to an ID, optionally creating the record if it doesn't exist\n */\n async resolveLookup(\n entityName: string, \n fieldName: string, \n fieldValue: string,\n autoCreate: boolean = false,\n createFields: Record<string, any> = {}\n ): Promise<string> {\n // Debug logging handled by caller if needed\n \n const rv = new RunView();\n const entityInfo = this.metadata.EntityByName(entityName);\n if (!entityInfo) {\n throw new Error(`Entity not found: ${entityName}`);\n }\n \n const field = entityInfo.Fields.find(f => f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());\n const quotes = field?.NeedsQuotes ? \"'\" : '';\n const result = await rv.RunView({\n EntityName: entityName,\n ExtraFilter: `${fieldName} = ${quotes}${fieldValue.replace(/'/g, \"''\")}${quotes}`,\n MaxRows: 1\n }, this.contextUser);\n \n if (result.Success && result.Results.length > 0) {\n if (entityInfo.PrimaryKeys.length > 0) {\n const pkeyField = entityInfo.PrimaryKeys[0].Name;\n const id = result.Results[0][pkeyField];\n return id;\n }\n }\n \n // If not found and auto-create is enabled, create the record\n if (autoCreate) {\n \n const newEntity = await this.metadata.GetEntityObject(entityName, this.contextUser);\n if (!newEntity) {\n throw new Error(`Failed to create entity object for: ${entityName}`);\n }\n \n newEntity.NewRecord();\n \n // Set the lookup field\n if (fieldName in newEntity) {\n (newEntity as any)[fieldName] = fieldValue;\n }\n \n // Set any additional fields provided\n for (const [key, value] of Object.entries(createFields)) {\n if (key in newEntity) {\n (newEntity as any)[key] = value;\n }\n }\n \n // Save the new record\n const saved = await newEntity.Save();\n if (!saved) {\n const errors = newEntity.LatestResult?.Errors?.join(', ') || 'Unknown error';\n throw new Error(`Failed to auto-create ${entityName}: ${errors}`);\n }\n \n // Return the new ID\n if (entityInfo.PrimaryKeys.length > 0) {\n const pkeyField = entityInfo.PrimaryKeys[0].Name;\n const newId = newEntity.Get(pkeyField);\n return newId;\n }\n }\n \n throw new Error(`Lookup failed: No record found in '${entityName}' where ${fieldName}='${fieldValue}'`);\n }\n \n /**\n * Build cascading defaults for a file path and process field values\n */\n async buildDefaults(filePath: string, entityConfig: EntityConfig): Promise<Record<string, any>> {\n const parts = path.dirname(filePath).split(path.sep);\n let defaults: Record<string, any> = { ...entityConfig.defaults };\n \n // Walk up the directory tree building defaults\n let currentPath = '';\n for (const part of parts) {\n currentPath = path.join(currentPath, part);\n const folderConfig = await this.loadFolderConfig(currentPath);\n \n if (folderConfig?.defaults) {\n defaults = { ...defaults, ...folderConfig.defaults };\n }\n }\n \n // Process all default values (lookups, file references, etc.)\n const processedDefaults: Record<string, any> = {};\n const baseDir = path.dirname(filePath);\n \n for (const [field, value] of Object.entries(defaults)) {\n try {\n processedDefaults[field] = await this.processFieldValue(value, baseDir, null, null);\n } catch (error) {\n throw new Error(`Failed to process default for field '${field}': ${error}`);\n }\n }\n \n return processedDefaults;\n }\n \n /**\n * Load folder configuration\n */\n private async 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 at ${configPath}:`, error);\n return null;\n }\n }\n \n return null;\n }\n \n /**\n * Calculate checksum for data\n */\n calculateChecksum(data: any): string {\n const hash = crypto.createHash('sha256');\n hash.update(JSON.stringify(data, null, 2));\n return hash.digest('hex');\n }\n \n /**\n * Get entity info by name\n */\n getEntityInfo(entityName: string): EntityInfo | null {\n return this.metadata.EntityByName(entityName);\n }\n \n /**\n * Create a new entity object\n */\n async createEntityObject(entityName: string): Promise<BaseEntity> {\n const entity = await this.metadata.GetEntityObject(entityName, this.contextUser);\n if (!entity) {\n throw new Error(`Failed to create entity object for: ${entityName}`);\n }\n return entity;\n }\n \n /**\n * Load an entity by primary key\n */\n async loadEntity(entityName: string, primaryKey: Record<string, any>): Promise<BaseEntity | null> {\n const entity = await this.createEntityObject(entityName);\n const entityInfo = this.getEntityInfo(entityName);\n \n if (!entityInfo) {\n throw new Error(`Entity not found: ${entityName}`);\n }\n \n // Handle composite keys\n const compositeKey = new CompositeKey();\n compositeKey.LoadFromSimpleObject(primaryKey);\n const loaded = await entity.InnerLoad(compositeKey);\n \n return loaded ? entity : null;\n }\n}"]}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
{
|
|
2
|
+
"commands": {
|
|
3
|
+
"init": {
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"args": {},
|
|
6
|
+
"description": "Initialize a directory for metadata synchronization",
|
|
7
|
+
"examples": [
|
|
8
|
+
"<%= config.bin %> <%= command.id %>"
|
|
9
|
+
],
|
|
10
|
+
"flags": {},
|
|
11
|
+
"hasDynamicHelp": false,
|
|
12
|
+
"hiddenAliases": [],
|
|
13
|
+
"id": "init",
|
|
14
|
+
"pluginAlias": "@memberjunction/metadata-sync",
|
|
15
|
+
"pluginName": "@memberjunction/metadata-sync",
|
|
16
|
+
"pluginType": "core",
|
|
17
|
+
"strict": true,
|
|
18
|
+
"enableJsonFlag": false,
|
|
19
|
+
"isESM": false,
|
|
20
|
+
"relativePath": [
|
|
21
|
+
"dist",
|
|
22
|
+
"commands",
|
|
23
|
+
"init",
|
|
24
|
+
"index.js"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"pull": {
|
|
28
|
+
"aliases": [],
|
|
29
|
+
"args": {},
|
|
30
|
+
"description": "Pull metadata from database to local files",
|
|
31
|
+
"examples": [
|
|
32
|
+
"<%= config.bin %> <%= command.id %> --entity=\"AI Prompts\"",
|
|
33
|
+
"<%= config.bin %> <%= command.id %> --entity=\"AI Prompts\" --filter=\"CategoryID='customer-service-id'\""
|
|
34
|
+
],
|
|
35
|
+
"flags": {
|
|
36
|
+
"entity": {
|
|
37
|
+
"description": "Entity name to pull",
|
|
38
|
+
"name": "entity",
|
|
39
|
+
"required": true,
|
|
40
|
+
"hasDynamicHelp": false,
|
|
41
|
+
"multiple": false,
|
|
42
|
+
"type": "option"
|
|
43
|
+
},
|
|
44
|
+
"filter": {
|
|
45
|
+
"description": "Additional filter for pulling specific records",
|
|
46
|
+
"name": "filter",
|
|
47
|
+
"hasDynamicHelp": false,
|
|
48
|
+
"multiple": false,
|
|
49
|
+
"type": "option"
|
|
50
|
+
},
|
|
51
|
+
"dry-run": {
|
|
52
|
+
"description": "Show what would be pulled without actually pulling",
|
|
53
|
+
"name": "dry-run",
|
|
54
|
+
"allowNo": false,
|
|
55
|
+
"type": "boolean"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"hasDynamicHelp": false,
|
|
59
|
+
"hiddenAliases": [],
|
|
60
|
+
"id": "pull",
|
|
61
|
+
"pluginAlias": "@memberjunction/metadata-sync",
|
|
62
|
+
"pluginName": "@memberjunction/metadata-sync",
|
|
63
|
+
"pluginType": "core",
|
|
64
|
+
"strict": true,
|
|
65
|
+
"enableJsonFlag": false,
|
|
66
|
+
"isESM": false,
|
|
67
|
+
"relativePath": [
|
|
68
|
+
"dist",
|
|
69
|
+
"commands",
|
|
70
|
+
"pull",
|
|
71
|
+
"index.js"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"push": {
|
|
75
|
+
"aliases": [],
|
|
76
|
+
"args": {},
|
|
77
|
+
"description": "Push local file changes to the database",
|
|
78
|
+
"examples": [
|
|
79
|
+
"<%= config.bin %> <%= command.id %>",
|
|
80
|
+
"<%= config.bin %> <%= command.id %> --dry-run",
|
|
81
|
+
"<%= config.bin %> <%= command.id %> --dir=\"ai-prompts\"",
|
|
82
|
+
"<%= config.bin %> <%= command.id %> --ci"
|
|
83
|
+
],
|
|
84
|
+
"flags": {
|
|
85
|
+
"dir": {
|
|
86
|
+
"description": "Specific entity directory to push",
|
|
87
|
+
"name": "dir",
|
|
88
|
+
"hasDynamicHelp": false,
|
|
89
|
+
"multiple": false,
|
|
90
|
+
"type": "option"
|
|
91
|
+
},
|
|
92
|
+
"dry-run": {
|
|
93
|
+
"description": "Show what would be pushed without actually pushing",
|
|
94
|
+
"name": "dry-run",
|
|
95
|
+
"allowNo": false,
|
|
96
|
+
"type": "boolean"
|
|
97
|
+
},
|
|
98
|
+
"ci": {
|
|
99
|
+
"description": "CI mode - no prompts, fail on issues",
|
|
100
|
+
"name": "ci",
|
|
101
|
+
"allowNo": false,
|
|
102
|
+
"type": "boolean"
|
|
103
|
+
},
|
|
104
|
+
"verbose": {
|
|
105
|
+
"char": "v",
|
|
106
|
+
"description": "Show detailed field-level output",
|
|
107
|
+
"name": "verbose",
|
|
108
|
+
"allowNo": false,
|
|
109
|
+
"type": "boolean"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"hasDynamicHelp": false,
|
|
113
|
+
"hiddenAliases": [],
|
|
114
|
+
"id": "push",
|
|
115
|
+
"pluginAlias": "@memberjunction/metadata-sync",
|
|
116
|
+
"pluginName": "@memberjunction/metadata-sync",
|
|
117
|
+
"pluginType": "core",
|
|
118
|
+
"strict": true,
|
|
119
|
+
"enableJsonFlag": false,
|
|
120
|
+
"isESM": false,
|
|
121
|
+
"relativePath": [
|
|
122
|
+
"dist",
|
|
123
|
+
"commands",
|
|
124
|
+
"push",
|
|
125
|
+
"index.js"
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"status": {
|
|
129
|
+
"aliases": [],
|
|
130
|
+
"args": {},
|
|
131
|
+
"description": "Show status of local files vs database",
|
|
132
|
+
"examples": [
|
|
133
|
+
"<%= config.bin %> <%= command.id %>",
|
|
134
|
+
"<%= config.bin %> <%= command.id %> --dir=\"ai-prompts\""
|
|
135
|
+
],
|
|
136
|
+
"flags": {
|
|
137
|
+
"dir": {
|
|
138
|
+
"description": "Specific entity directory to check status",
|
|
139
|
+
"name": "dir",
|
|
140
|
+
"hasDynamicHelp": false,
|
|
141
|
+
"multiple": false,
|
|
142
|
+
"type": "option"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"hasDynamicHelp": false,
|
|
146
|
+
"hiddenAliases": [],
|
|
147
|
+
"id": "status",
|
|
148
|
+
"pluginAlias": "@memberjunction/metadata-sync",
|
|
149
|
+
"pluginName": "@memberjunction/metadata-sync",
|
|
150
|
+
"pluginType": "core",
|
|
151
|
+
"strict": true,
|
|
152
|
+
"enableJsonFlag": false,
|
|
153
|
+
"isESM": false,
|
|
154
|
+
"relativePath": [
|
|
155
|
+
"dist",
|
|
156
|
+
"commands",
|
|
157
|
+
"status",
|
|
158
|
+
"index.js"
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
"watch": {
|
|
162
|
+
"aliases": [],
|
|
163
|
+
"args": {},
|
|
164
|
+
"description": "Watch for file changes and automatically push to database",
|
|
165
|
+
"examples": [
|
|
166
|
+
"<%= config.bin %> <%= command.id %>",
|
|
167
|
+
"<%= config.bin %> <%= command.id %> --dir=\"ai-prompts\""
|
|
168
|
+
],
|
|
169
|
+
"flags": {
|
|
170
|
+
"dir": {
|
|
171
|
+
"description": "Specific entity directory to watch",
|
|
172
|
+
"name": "dir",
|
|
173
|
+
"hasDynamicHelp": false,
|
|
174
|
+
"multiple": false,
|
|
175
|
+
"type": "option"
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"hasDynamicHelp": false,
|
|
179
|
+
"hiddenAliases": [],
|
|
180
|
+
"id": "watch",
|
|
181
|
+
"pluginAlias": "@memberjunction/metadata-sync",
|
|
182
|
+
"pluginName": "@memberjunction/metadata-sync",
|
|
183
|
+
"pluginType": "core",
|
|
184
|
+
"strict": true,
|
|
185
|
+
"enableJsonFlag": false,
|
|
186
|
+
"isESM": false,
|
|
187
|
+
"relativePath": [
|
|
188
|
+
"dist",
|
|
189
|
+
"commands",
|
|
190
|
+
"watch",
|
|
191
|
+
"index.js"
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"version": "2.46.0"
|
|
196
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@memberjunction/metadata-sync",
|
|
3
|
+
"version": "2.46.0",
|
|
4
|
+
"description": "MemberJunction metadata synchronization CLI tool",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"oclif",
|
|
7
|
+
"metadata",
|
|
8
|
+
"sync"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/MemberJunction/MJ",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/MemberJunction/MJ/issues"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/MemberJunction/MJ.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"author": "MemberJunction",
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"bin": {
|
|
23
|
+
"mj-sync": "bin/run.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"/bin",
|
|
27
|
+
"/dist",
|
|
28
|
+
"/oclif.manifest.json"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -b",
|
|
32
|
+
"prepack": "npm run build && oclif manifest && oclif readme",
|
|
33
|
+
"postpack": "rimraf oclif.manifest.json",
|
|
34
|
+
"version": "oclif readme && git add README.md"
|
|
35
|
+
},
|
|
36
|
+
"oclif": {
|
|
37
|
+
"bin": "mj-sync",
|
|
38
|
+
"commands": "./dist/commands",
|
|
39
|
+
"hooks": {
|
|
40
|
+
"init": "./dist/hooks/init"
|
|
41
|
+
},
|
|
42
|
+
"dirname": "mj-sync",
|
|
43
|
+
"plugins": [
|
|
44
|
+
"@oclif/plugin-help",
|
|
45
|
+
"@oclif/plugin-warn-if-update-available",
|
|
46
|
+
"@oclif/plugin-version"
|
|
47
|
+
],
|
|
48
|
+
"warn-if-update-available": {
|
|
49
|
+
"timeoutInDays": 1
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@inquirer/prompts": "^5.0.1",
|
|
54
|
+
"@memberjunction/core": "2.46.0",
|
|
55
|
+
"@memberjunction/core-entities": "2.46.0",
|
|
56
|
+
"@memberjunction/core-entities-server": "2.46.0",
|
|
57
|
+
"@memberjunction/sqlserver-dataprovider": "2.46.0",
|
|
58
|
+
"@memberjunction/graphql-dataprovider": "2.46.0",
|
|
59
|
+
"@oclif/core": "^3",
|
|
60
|
+
"@oclif/plugin-help": "^6",
|
|
61
|
+
"@oclif/plugin-version": "^2.0.17",
|
|
62
|
+
"@oclif/plugin-warn-if-update-available": "^3.0.16",
|
|
63
|
+
"chokidar": "^3.6.0",
|
|
64
|
+
"cosmiconfig": "9.0.0",
|
|
65
|
+
"dotenv": "16.4.5",
|
|
66
|
+
"fast-glob": "^3.3.2",
|
|
67
|
+
"fs-extra": "^11.2.0",
|
|
68
|
+
"ora-classic": "^5.4.2",
|
|
69
|
+
"zod": "^3.23.4",
|
|
70
|
+
"crypto": "^1.0.1",
|
|
71
|
+
"axios": "^1.6.8",
|
|
72
|
+
"typeorm": "^0.3.20"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@oclif/prettier-config": "^0.2.1",
|
|
76
|
+
"@types/fs-extra": "^11.0.4",
|
|
77
|
+
"oclif": "^4",
|
|
78
|
+
"rimraf": "5.0.7",
|
|
79
|
+
"ts-node": "^10.9.2",
|
|
80
|
+
"typescript": "^5.4.5"
|
|
81
|
+
},
|
|
82
|
+
"engines": {
|
|
83
|
+
"node": ">=20.0.0"
|
|
84
|
+
}
|
|
85
|
+
}
|