@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,210 @@
|
|
|
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
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
10
|
+
const ora_classic_1 = __importDefault(require("ora-classic"));
|
|
11
|
+
const config_1 = require("../../config");
|
|
12
|
+
const sync_engine_1 = require("../../lib/sync-engine");
|
|
13
|
+
const provider_utils_1 = require("../../lib/provider-utils");
|
|
14
|
+
class Watch extends core_1.Command {
|
|
15
|
+
static description = 'Watch for file changes and automatically push to database';
|
|
16
|
+
static examples = [
|
|
17
|
+
`<%= config.bin %> <%= command.id %>`,
|
|
18
|
+
`<%= config.bin %> <%= command.id %> --dir="ai-prompts"`,
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
dir: core_1.Flags.string({ description: 'Specific entity directory to watch' }),
|
|
22
|
+
};
|
|
23
|
+
syncEngine;
|
|
24
|
+
syncConfig;
|
|
25
|
+
debounceTimers = new Map();
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(Watch);
|
|
28
|
+
const spinner = (0, ora_classic_1.default)();
|
|
29
|
+
try {
|
|
30
|
+
// Load configurations
|
|
31
|
+
spinner.start('Loading configuration');
|
|
32
|
+
const mjConfig = (0, config_1.loadMJConfig)();
|
|
33
|
+
if (!mjConfig) {
|
|
34
|
+
this.error('No mj.config.cjs found in current directory or parent directories');
|
|
35
|
+
}
|
|
36
|
+
this.syncConfig = await (0, config_1.loadSyncConfig)(process.cwd());
|
|
37
|
+
// Initialize data provider
|
|
38
|
+
const provider = await (0, provider_utils_1.initializeProvider)(mjConfig);
|
|
39
|
+
// Initialize sync engine
|
|
40
|
+
this.syncEngine = new sync_engine_1.SyncEngine((0, provider_utils_1.getSystemUser)());
|
|
41
|
+
await this.syncEngine.initialize();
|
|
42
|
+
spinner.succeed('Configuration loaded');
|
|
43
|
+
// Find entity directories to watch
|
|
44
|
+
const entityDirs = (0, provider_utils_1.findEntityDirectories)(process.cwd(), flags.dir);
|
|
45
|
+
if (entityDirs.length === 0) {
|
|
46
|
+
this.error('No entity directories found');
|
|
47
|
+
}
|
|
48
|
+
this.log(`Watching ${entityDirs.length} entity ${entityDirs.length === 1 ? 'directory' : 'directories'} for changes`);
|
|
49
|
+
// Set up watchers
|
|
50
|
+
const watchers = [];
|
|
51
|
+
for (const entityDir of entityDirs) {
|
|
52
|
+
const entityConfig = await (0, config_1.loadEntityConfig)(entityDir);
|
|
53
|
+
if (!entityConfig) {
|
|
54
|
+
this.warn(`Skipping ${entityDir} - no valid entity configuration`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
this.log(`Watching ${entityConfig.entity} in ${entityDir}`);
|
|
58
|
+
// Watch for JSON files and external files
|
|
59
|
+
const patterns = [
|
|
60
|
+
path_1.default.join(entityDir, entityConfig.filePattern || '*.json'),
|
|
61
|
+
path_1.default.join(entityDir, '**/*.md'),
|
|
62
|
+
path_1.default.join(entityDir, '**/*.txt'),
|
|
63
|
+
path_1.default.join(entityDir, '**/*.html'),
|
|
64
|
+
path_1.default.join(entityDir, '**/*.liquid'),
|
|
65
|
+
path_1.default.join(entityDir, '**/*.sql')
|
|
66
|
+
];
|
|
67
|
+
const ignored = [
|
|
68
|
+
'**/node_modules/**',
|
|
69
|
+
'**/.git/**',
|
|
70
|
+
'**/.mj-sync.json',
|
|
71
|
+
'**/.mj-folder.json',
|
|
72
|
+
...(this.syncConfig?.watch?.ignorePatterns || [])
|
|
73
|
+
];
|
|
74
|
+
const watcher = chokidar_1.default.watch(patterns, {
|
|
75
|
+
ignored,
|
|
76
|
+
persistent: true,
|
|
77
|
+
ignoreInitial: true
|
|
78
|
+
});
|
|
79
|
+
watcher
|
|
80
|
+
.on('add', (filePath) => this.handleFileChange(filePath, 'added', entityDir, entityConfig))
|
|
81
|
+
.on('change', (filePath) => this.handleFileChange(filePath, 'changed', entityDir, entityConfig))
|
|
82
|
+
.on('unlink', (filePath) => this.handleFileChange(filePath, 'deleted', entityDir, entityConfig));
|
|
83
|
+
watchers.push(watcher);
|
|
84
|
+
}
|
|
85
|
+
this.log('\nPress Ctrl+C to stop watching');
|
|
86
|
+
// Keep process alive
|
|
87
|
+
process.stdin.resume();
|
|
88
|
+
// Cleanup on exit
|
|
89
|
+
process.on('SIGINT', () => {
|
|
90
|
+
this.log('\nStopping watchers...');
|
|
91
|
+
watchers.forEach(w => w.close());
|
|
92
|
+
process.exit(0);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
spinner.fail('Watch failed');
|
|
97
|
+
this.error(error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async handleFileChange(filePath, event, entityDir, entityConfig) {
|
|
101
|
+
// Clear existing debounce timer
|
|
102
|
+
const existingTimer = this.debounceTimers.get(filePath);
|
|
103
|
+
if (existingTimer) {
|
|
104
|
+
clearTimeout(existingTimer);
|
|
105
|
+
}
|
|
106
|
+
// Set new debounce timer
|
|
107
|
+
const debounceMs = this.syncConfig?.watch?.debounceMs || 1000;
|
|
108
|
+
const timer = setTimeout(async () => {
|
|
109
|
+
this.debounceTimers.delete(filePath);
|
|
110
|
+
try {
|
|
111
|
+
const relativePath = path_1.default.relative(entityDir, filePath);
|
|
112
|
+
this.log(`\nFile ${event}: ${relativePath}`);
|
|
113
|
+
if (event === 'deleted') {
|
|
114
|
+
// Handle deletion
|
|
115
|
+
this.log('File deletion detected - manual database cleanup may be required');
|
|
116
|
+
}
|
|
117
|
+
else if (filePath.endsWith('.json')) {
|
|
118
|
+
// Handle JSON file change
|
|
119
|
+
await this.syncJsonFile(filePath, entityDir, entityConfig);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Handle external file change
|
|
123
|
+
await this.syncExternalFile(filePath, entityDir, entityConfig);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
this.warn(`Failed to sync ${filePath}: ${error.message || error}`);
|
|
128
|
+
}
|
|
129
|
+
}, debounceMs);
|
|
130
|
+
this.debounceTimers.set(filePath, timer);
|
|
131
|
+
}
|
|
132
|
+
async syncJsonFile(filePath, entityDir, entityConfig) {
|
|
133
|
+
const recordData = await fs_extra_1.default.readJson(filePath);
|
|
134
|
+
// Build defaults
|
|
135
|
+
const defaults = await this.syncEngine.buildDefaults(filePath, entityConfig);
|
|
136
|
+
// Load or create entity
|
|
137
|
+
let entity = null;
|
|
138
|
+
let isNew = false;
|
|
139
|
+
if (recordData.primaryKey) {
|
|
140
|
+
entity = await this.syncEngine.loadEntity(entityConfig.entity, recordData.primaryKey);
|
|
141
|
+
}
|
|
142
|
+
if (!entity) {
|
|
143
|
+
// New record
|
|
144
|
+
entity = await this.syncEngine.createEntityObject(entityConfig.entity);
|
|
145
|
+
entity.NewRecord();
|
|
146
|
+
isNew = true;
|
|
147
|
+
}
|
|
148
|
+
// Apply defaults first
|
|
149
|
+
for (const [field, value] of Object.entries(defaults)) {
|
|
150
|
+
if (field in entity) {
|
|
151
|
+
entity[field] = value;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Apply record fields
|
|
155
|
+
for (const [field, value] of Object.entries(recordData.fields)) {
|
|
156
|
+
if (field in entity) {
|
|
157
|
+
const processedValue = await this.syncEngine.processFieldValue(value, path_1.default.dirname(filePath));
|
|
158
|
+
entity[field] = processedValue;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Save the record
|
|
162
|
+
const saved = await entity.Save();
|
|
163
|
+
if (!saved) {
|
|
164
|
+
const errors = entity.LatestResult?.Errors?.join(', ') || 'Unknown error';
|
|
165
|
+
throw new Error(`Failed to save record: ${errors}`);
|
|
166
|
+
}
|
|
167
|
+
this.log(`Successfully ${isNew ? 'created' : 'updated'} ${entityConfig.entity} record`);
|
|
168
|
+
// Update the local file with new primary key if created
|
|
169
|
+
if (isNew) {
|
|
170
|
+
const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);
|
|
171
|
+
if (entityInfo) {
|
|
172
|
+
const newPrimaryKey = {};
|
|
173
|
+
for (const pk of entityInfo.PrimaryKeys) {
|
|
174
|
+
newPrimaryKey[pk.Name] = entity.Get(pk.Name);
|
|
175
|
+
}
|
|
176
|
+
recordData.primaryKey = newPrimaryKey;
|
|
177
|
+
// Update sync metadata
|
|
178
|
+
recordData.sync = {
|
|
179
|
+
lastModified: new Date().toISOString(),
|
|
180
|
+
checksum: this.syncEngine.calculateChecksum(recordData.fields)
|
|
181
|
+
};
|
|
182
|
+
// Write back to file
|
|
183
|
+
await fs_extra_1.default.writeJson(filePath, recordData, { spaces: 2 });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async syncExternalFile(filePath, entityDir, entityConfig) {
|
|
188
|
+
// Find the corresponding JSON file
|
|
189
|
+
const fileName = path_1.default.basename(filePath);
|
|
190
|
+
const parts = fileName.split('.');
|
|
191
|
+
if (parts.length >= 3) {
|
|
192
|
+
// Format: uuid.fieldname.ext
|
|
193
|
+
const jsonFileName = `${parts[0]}.json`;
|
|
194
|
+
const fieldName = parts[1];
|
|
195
|
+
const jsonFilePath = path_1.default.join(path_1.default.dirname(filePath), jsonFileName);
|
|
196
|
+
if (await fs_extra_1.default.pathExists(jsonFilePath)) {
|
|
197
|
+
// Update the JSON file's sync metadata to trigger a sync
|
|
198
|
+
const recordData = await fs_extra_1.default.readJson(jsonFilePath);
|
|
199
|
+
recordData.sync = {
|
|
200
|
+
lastModified: new Date().toISOString(),
|
|
201
|
+
checksum: recordData.sync?.checksum || ''
|
|
202
|
+
};
|
|
203
|
+
await fs_extra_1.default.writeJson(jsonFilePath, recordData, { spaces: 2 });
|
|
204
|
+
this.log(`Updated sync metadata for ${jsonFileName} due to external file change`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.default = Watch;
|
|
210
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/watch/index.ts"],"names":[],"mappings":";;;;;AAAA,sCAA6C;AAC7C,wDAA0B;AAC1B,gDAAwB;AACxB,wDAAgC;AAChC,8DAA8B;AAC9B,yCAA8E;AAC9E,uDAA+D;AAC/D,6DAAoG;AAGpG,MAAqB,KAAM,SAAQ,cAAO;IACxC,MAAM,CAAC,WAAW,GAAG,2DAA2D,CAAC;IAEjF,MAAM,CAAC,QAAQ,GAAG;QAChB,qCAAqC;QACrC,wDAAwD;KACzD,CAAC;IAEF,MAAM,CAAC,KAAK,GAAG;QACb,GAAG,EAAE,YAAK,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;KACzE,CAAC;IAEM,UAAU,CAAc;IACxB,UAAU,CAAM;IAChB,cAAc,GAAgC,IAAI,GAAG,EAAE,CAAC;IAEhE,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,IAAA,qBAAG,GAAE,CAAC;QAEtB,IAAI,CAAC;YACH,sBAAsB;YACtB,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,IAAA,qBAAY,GAAE,CAAC;YAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,IAAI,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;YAClF,CAAC;YAED,IAAI,CAAC,UAAU,GAAG,MAAM,IAAA,uBAAc,EAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YAEtD,2BAA2B;YAC3B,MAAM,QAAQ,GAAG,MAAM,IAAA,mCAAkB,EAAC,QAAQ,CAAC,CAAC;YAEpD,yBAAyB;YACzB,IAAI,CAAC,UAAU,GAAG,IAAI,wBAAU,CAAC,IAAA,8BAAa,GAAE,CAAC,CAAC;YAClD,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;YACnC,OAAO,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;YAExC,mCAAmC;YACnC,MAAM,UAAU,GAAG,IAAA,sCAAqB,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAEnE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;YAC5C,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,YAAY,UAAU,CAAC,MAAM,WAAW,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,cAAc,CAAC,CAAC;YAEtH,kBAAkB;YAClB,MAAM,QAAQ,GAAyB,EAAE,CAAC;YAE1C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;gBACnC,MAAM,YAAY,GAAG,MAAM,IAAA,yBAAgB,EAAC,SAAS,CAAC,CAAC;gBACvD,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,IAAI,CAAC,IAAI,CAAC,YAAY,SAAS,kCAAkC,CAAC,CAAC;oBACnE,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,GAAG,CAAC,YAAY,YAAY,CAAC,MAAM,OAAO,SAAS,EAAE,CAAC,CAAC;gBAE5D,0CAA0C;gBAC1C,MAAM,QAAQ,GAAG;oBACf,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,WAAW,IAAI,QAAQ,CAAC;oBAC1D,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC;oBAC/B,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;oBAChC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC;oBACjC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC;oBACnC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;iBACjC,CAAC;gBAEF,MAAM,OAAO,GAAG;oBACd,oBAAoB;oBACpB,YAAY;oBACZ,kBAAkB;oBAClB,oBAAoB;oBACpB,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,cAAc,IAAI,EAAE,CAAC;iBAClD,CAAC;gBAEF,MAAM,OAAO,GAAG,kBAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE;oBACvC,OAAO;oBACP,UAAU,EAAE,IAAI;oBAChB,aAAa,EAAE,IAAI;iBACpB,CAAC,CAAC;gBAEH,OAAO;qBACJ,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;qBAC1F,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;qBAC/F,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;gBAEnG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;YAE5C,qBAAqB;YACrB,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAEvB,kBAAkB;YAClB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACxB,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;gBACnC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC;QAEL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC7B,IAAI,CAAC,KAAK,CAAC,KAAc,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAC5B,QAAgB,EAChB,KAAa,EACb,SAAiB,EACjB,YAAiB;QAEjB,gCAAgC;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,aAAa,EAAE,CAAC;YAClB,YAAY,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,yBAAyB;QACzB,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,IAAI,IAAI,CAAC;QAC9D,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YAClC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAErC,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,cAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;gBACxD,IAAI,CAAC,GAAG,CAAC,UAAU,KAAK,KAAK,YAAY,EAAE,CAAC,CAAC;gBAE7C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,kBAAkB;oBAClB,IAAI,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;gBAC/E,CAAC;qBAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBACtC,0BAA0B;oBAC1B,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;gBAC7D,CAAC;qBAAM,CAAC;oBACN,8BAA8B;oBAC9B,MAAM,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,kBAAkB,QAAQ,KAAM,KAAa,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC,EAAE,UAAU,CAAC,CAAC;QAEf,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAEO,KAAK,CAAC,YAAY,CACxB,QAAgB,EAChB,SAAiB,EACjB,YAAiB;QAEjB,MAAM,UAAU,GAAe,MAAM,kBAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAE3D,iBAAiB;QACjB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAE7E,wBAAwB;QACxB,IAAI,MAAM,GAAsB,IAAI,CAAC;QACrC,IAAI,KAAK,GAAG,KAAK,CAAC;QAElB,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;QACxF,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,aAAa;YACb,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YACvE,MAAM,CAAC,SAAS,EAAE,CAAC;YACnB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;QAED,uBAAuB;QACvB,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtD,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;gBACnB,MAAc,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;YACjC,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/D,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;gBACpB,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,EAAE,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC7F,MAAc,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC;YAC1C,CAAC;QACH,CAAC;QAED,kBAAkB;QAClB,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC;YAC1E,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;QACtD,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,gBAAgB,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,IAAI,YAAY,CAAC,MAAM,SAAS,CAAC,CAAC;QAExF,wDAAwD;QACxD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YACtE,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,aAAa,GAAwB,EAAE,CAAC;gBAC9C,KAAK,MAAM,EAAE,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;oBACxC,aAAa,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;gBAC/C,CAAC;gBACD,UAAU,CAAC,UAAU,GAAG,aAAa,CAAC;gBAEtC,uBAAuB;gBACvB,UAAU,CAAC,IAAI,GAAG;oBAChB,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACtC,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,UAAU,CAAC,MAAM,CAAC;iBAC/D,CAAC;gBAEF,qBAAqB;gBACrB,MAAM,kBAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAC5B,QAAgB,EAChB,SAAiB,EACjB,YAAiB;QAEjB,mCAAmC;QACnC,MAAM,QAAQ,GAAG,cAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACtB,6BAA6B;YAC7B,MAAM,YAAY,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC;YAErE,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBACtC,yDAAyD;gBACzD,MAAM,UAAU,GAAe,MAAM,kBAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;gBAC/D,UAAU,CAAC,IAAI,GAAG;oBAChB,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACtC,QAAQ,EAAE,UAAU,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE;iBAC1C,CAAC;gBACF,MAAM,kBAAE,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;gBAE5D,IAAI,CAAC,GAAG,CAAC,6BAA6B,YAAY,8BAA8B,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;IACH,CAAC;;AAtPH,wBAuPC","sourcesContent":["import { Command, Flags } from '@oclif/core';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport chokidar from 'chokidar';\nimport ora from 'ora-classic';\nimport { loadMJConfig, loadSyncConfig, loadEntityConfig } from '../../config';\nimport { SyncEngine, RecordData } from '../../lib/sync-engine';\nimport { initializeProvider, findEntityDirectories, getSystemUser } from '../../lib/provider-utils';\nimport { BaseEntity } from '@memberjunction/core';\n\nexport default class Watch extends Command {\n static description = 'Watch for file changes and automatically push to database';\n \n static examples = [\n `<%= config.bin %> <%= command.id %>`,\n `<%= config.bin %> <%= command.id %> --dir=\"ai-prompts\"`,\n ];\n \n static flags = {\n dir: Flags.string({ description: 'Specific entity directory to watch' }),\n };\n \n private syncEngine!: SyncEngine;\n private syncConfig: any;\n private debounceTimers: Map<string, NodeJS.Timeout> = new Map();\n \n async run(): Promise<void> {\n const { flags } = await this.parse(Watch);\n const spinner = ora();\n \n try {\n // Load configurations\n spinner.start('Loading configuration');\n const mjConfig = loadMJConfig();\n if (!mjConfig) {\n this.error('No mj.config.cjs found in current directory or parent directories');\n }\n \n this.syncConfig = await loadSyncConfig(process.cwd());\n \n // Initialize data provider\n const provider = await initializeProvider(mjConfig);\n \n // Initialize sync engine\n this.syncEngine = new SyncEngine(getSystemUser());\n await this.syncEngine.initialize();\n spinner.succeed('Configuration loaded');\n \n // Find entity directories to watch\n const entityDirs = findEntityDirectories(process.cwd(), flags.dir);\n \n if (entityDirs.length === 0) {\n this.error('No entity directories found');\n }\n \n this.log(`Watching ${entityDirs.length} entity ${entityDirs.length === 1 ? 'directory' : 'directories'} for changes`);\n \n // Set up watchers\n const watchers: chokidar.FSWatcher[] = [];\n \n for (const entityDir of entityDirs) {\n const entityConfig = await loadEntityConfig(entityDir);\n if (!entityConfig) {\n this.warn(`Skipping ${entityDir} - no valid entity configuration`);\n continue;\n }\n \n this.log(`Watching ${entityConfig.entity} in ${entityDir}`);\n \n // Watch for JSON files and external files\n const patterns = [\n path.join(entityDir, entityConfig.filePattern || '*.json'),\n path.join(entityDir, '**/*.md'),\n path.join(entityDir, '**/*.txt'),\n path.join(entityDir, '**/*.html'),\n path.join(entityDir, '**/*.liquid'),\n path.join(entityDir, '**/*.sql')\n ];\n \n const ignored = [\n '**/node_modules/**',\n '**/.git/**',\n '**/.mj-sync.json',\n '**/.mj-folder.json',\n ...(this.syncConfig?.watch?.ignorePatterns || [])\n ];\n \n const watcher = chokidar.watch(patterns, {\n ignored,\n persistent: true,\n ignoreInitial: true\n });\n \n watcher\n .on('add', (filePath) => this.handleFileChange(filePath, 'added', entityDir, entityConfig))\n .on('change', (filePath) => this.handleFileChange(filePath, 'changed', entityDir, entityConfig))\n .on('unlink', (filePath) => this.handleFileChange(filePath, 'deleted', entityDir, entityConfig));\n \n watchers.push(watcher);\n }\n \n this.log('\\nPress Ctrl+C to stop watching');\n \n // Keep process alive\n process.stdin.resume();\n \n // Cleanup on exit\n process.on('SIGINT', () => {\n this.log('\\nStopping watchers...');\n watchers.forEach(w => w.close());\n process.exit(0);\n });\n \n } catch (error) {\n spinner.fail('Watch failed');\n this.error(error as Error);\n }\n }\n \n private async handleFileChange(\n filePath: string,\n event: string,\n entityDir: string,\n entityConfig: any\n ): Promise<void> {\n // Clear existing debounce timer\n const existingTimer = this.debounceTimers.get(filePath);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n \n // Set new debounce timer\n const debounceMs = this.syncConfig?.watch?.debounceMs || 1000;\n const timer = setTimeout(async () => {\n this.debounceTimers.delete(filePath);\n \n try {\n const relativePath = path.relative(entityDir, filePath);\n this.log(`\\nFile ${event}: ${relativePath}`);\n \n if (event === 'deleted') {\n // Handle deletion\n this.log('File deletion detected - manual database cleanup may be required');\n } else if (filePath.endsWith('.json')) {\n // Handle JSON file change\n await this.syncJsonFile(filePath, entityDir, entityConfig);\n } else {\n // Handle external file change\n await this.syncExternalFile(filePath, entityDir, entityConfig);\n }\n } catch (error) {\n this.warn(`Failed to sync ${filePath}: ${(error as any).message || error}`);\n }\n }, debounceMs);\n \n this.debounceTimers.set(filePath, timer);\n }\n \n private async syncJsonFile(\n filePath: string,\n entityDir: string,\n entityConfig: any\n ): Promise<void> {\n const recordData: RecordData = await fs.readJson(filePath);\n \n // Build defaults\n const defaults = await this.syncEngine.buildDefaults(filePath, entityConfig);\n \n // Load or create entity\n let entity: BaseEntity | null = null;\n let isNew = false;\n \n if (recordData.primaryKey) {\n entity = await this.syncEngine.loadEntity(entityConfig.entity, recordData.primaryKey);\n }\n \n if (!entity) {\n // New record\n entity = await this.syncEngine.createEntityObject(entityConfig.entity);\n entity.NewRecord();\n isNew = true;\n }\n \n // Apply defaults first\n for (const [field, value] of Object.entries(defaults)) {\n if (field in entity) {\n (entity as any)[field] = value;\n }\n }\n \n // Apply record fields\n for (const [field, value] of Object.entries(recordData.fields)) {\n if (field in entity) {\n const processedValue = await this.syncEngine.processFieldValue(value, path.dirname(filePath));\n (entity as any)[field] = processedValue;\n }\n }\n \n // Save the record\n const saved = await entity.Save();\n if (!saved) {\n const errors = entity.LatestResult?.Errors?.join(', ') || 'Unknown error';\n throw new Error(`Failed to save record: ${errors}`);\n }\n \n this.log(`Successfully ${isNew ? 'created' : 'updated'} ${entityConfig.entity} record`);\n \n // Update the local file with new primary key if created\n if (isNew) {\n const entityInfo = this.syncEngine.getEntityInfo(entityConfig.entity);\n if (entityInfo) {\n const newPrimaryKey: Record<string, any> = {};\n for (const pk of entityInfo.PrimaryKeys) {\n newPrimaryKey[pk.Name] = entity.Get(pk.Name);\n }\n recordData.primaryKey = newPrimaryKey;\n \n // Update sync metadata\n recordData.sync = {\n lastModified: new Date().toISOString(),\n checksum: this.syncEngine.calculateChecksum(recordData.fields)\n };\n \n // Write back to file\n await fs.writeJson(filePath, recordData, { spaces: 2 });\n }\n }\n }\n \n private async syncExternalFile(\n filePath: string,\n entityDir: string,\n entityConfig: any\n ): Promise<void> {\n // Find the corresponding JSON file\n const fileName = path.basename(filePath);\n const parts = fileName.split('.');\n \n if (parts.length >= 3) {\n // Format: uuid.fieldname.ext\n const jsonFileName = `${parts[0]}.json`;\n const fieldName = parts[1];\n const jsonFilePath = path.join(path.dirname(filePath), jsonFileName);\n \n if (await fs.pathExists(jsonFilePath)) {\n // Update the JSON file's sync metadata to trigger a sync\n const recordData: RecordData = await fs.readJson(jsonFilePath);\n recordData.sync = {\n lastModified: new Date().toISOString(),\n checksum: recordData.sync?.checksum || ''\n };\n await fs.writeJson(jsonFilePath, recordData, { spaces: 2 });\n \n this.log(`Updated sync metadata for ${jsonFileName} due to external file change`);\n }\n }\n }\n}"]}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface MJConfig {
|
|
2
|
+
dbHost: string;
|
|
3
|
+
dbPort?: number;
|
|
4
|
+
dbDatabase: string;
|
|
5
|
+
dbUsername: string;
|
|
6
|
+
dbPassword: string;
|
|
7
|
+
dbTrustServerCertificate?: string;
|
|
8
|
+
dbInstanceName?: string;
|
|
9
|
+
mjCoreSchema?: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
export interface SyncConfig {
|
|
13
|
+
version: string;
|
|
14
|
+
push?: {
|
|
15
|
+
validateBeforePush?: boolean;
|
|
16
|
+
requireConfirmation?: boolean;
|
|
17
|
+
};
|
|
18
|
+
watch?: {
|
|
19
|
+
debounceMs?: number;
|
|
20
|
+
ignorePatterns?: string[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface RelatedEntityConfig {
|
|
24
|
+
entity: string;
|
|
25
|
+
foreignKey: string;
|
|
26
|
+
filter?: string;
|
|
27
|
+
relatedEntities?: Record<string, RelatedEntityConfig>;
|
|
28
|
+
}
|
|
29
|
+
export interface EntityConfig {
|
|
30
|
+
entity: string;
|
|
31
|
+
filePattern?: string;
|
|
32
|
+
defaults?: Record<string, any>;
|
|
33
|
+
pull?: {
|
|
34
|
+
filter?: string;
|
|
35
|
+
relatedEntities?: Record<string, RelatedEntityConfig>;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export interface FolderConfig {
|
|
39
|
+
defaults: Record<string, any>;
|
|
40
|
+
}
|
|
41
|
+
export declare function loadMJConfig(): MJConfig | null;
|
|
42
|
+
export declare function loadSyncConfig(dir: string): Promise<SyncConfig | null>;
|
|
43
|
+
export declare function loadEntityConfig(dir: string): Promise<EntityConfig | null>;
|
|
44
|
+
export declare function loadFolderConfig(dir: string): Promise<FolderConfig | null>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
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.loadFolderConfig = exports.loadEntityConfig = exports.loadSyncConfig = exports.loadMJConfig = void 0;
|
|
7
|
+
const cosmiconfig_1 = require("cosmiconfig");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
+
function loadMJConfig() {
|
|
11
|
+
try {
|
|
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
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.loadMJConfig = loadMJConfig;
|
|
25
|
+
async function loadSyncConfig(dir) {
|
|
26
|
+
const configPath = path_1.default.join(dir, '.mj-sync.json');
|
|
27
|
+
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
28
|
+
try {
|
|
29
|
+
return await fs_extra_1.default.readJson(configPath);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error('Error loading sync config:', error);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
exports.loadSyncConfig = loadSyncConfig;
|
|
39
|
+
async function loadEntityConfig(dir) {
|
|
40
|
+
const configPath = path_1.default.join(dir, '.mj-sync.json');
|
|
41
|
+
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
42
|
+
try {
|
|
43
|
+
const config = await fs_extra_1.default.readJson(configPath);
|
|
44
|
+
if (config.entity) {
|
|
45
|
+
return config;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('Error loading entity config:', error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
exports.loadEntityConfig = loadEntityConfig;
|
|
55
|
+
async function loadFolderConfig(dir) {
|
|
56
|
+
const configPath = path_1.default.join(dir, '.mj-folder.json');
|
|
57
|
+
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
58
|
+
try {
|
|
59
|
+
return await fs_extra_1.default.readJson(configPath);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('Error loading folder config:', error);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
exports.loadFolderConfig = loadFolderConfig;
|
|
69
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +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}"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const core_entities_server_1 = require("@memberjunction/core-entities-server");
|
|
27
|
+
const dotenv = __importStar(require("dotenv"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const provider_utils_1 = require("../lib/provider-utils");
|
|
30
|
+
const hook = async function () {
|
|
31
|
+
// Load .env from the repository root
|
|
32
|
+
dotenv.config({ path: path.join(__dirname, '../../../../.env') });
|
|
33
|
+
// Load core entities server subclasses
|
|
34
|
+
(0, core_entities_server_1.LoadCoreEntitiesServerSubClasses)();
|
|
35
|
+
// Register cleanup handlers
|
|
36
|
+
process.on('exit', () => {
|
|
37
|
+
(0, provider_utils_1.cleanupProvider)().catch(() => {
|
|
38
|
+
// Ignore errors during cleanup
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
process.on('SIGINT', async () => {
|
|
42
|
+
await (0, provider_utils_1.cleanupProvider)();
|
|
43
|
+
process.exit(0);
|
|
44
|
+
});
|
|
45
|
+
process.on('SIGTERM', async () => {
|
|
46
|
+
await (0, provider_utils_1.cleanupProvider)();
|
|
47
|
+
process.exit(0);
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
exports.default = hook;
|
|
51
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/hooks/init.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AACA,+EAAwF;AACxF,+CAAiC;AACjC,2CAA6B;AAC7B,0DAAwD;AAExD,MAAM,IAAI,GAAiB,KAAK;IAC9B,qCAAqC;IACrC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;IAElE,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';\n\nconst hook: Hook<'init'> = async function () {\n // Load .env from the repository root\n dotenv.config({ path: path.join(__dirname, '../../../../.env') });\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;"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.run = void 0;
|
|
4
|
+
var core_1 = require("@oclif/core");
|
|
5
|
+
Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oCAAkC;AAAzB,2FAAA,GAAG,OAAA","sourcesContent":["export { run } from '@oclif/core';"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
|
|
2
|
+
import type { MJConfig } from '../config';
|
|
3
|
+
import { UserInfo } from '@memberjunction/core';
|
|
4
|
+
export declare function initializeProvider(config: MJConfig): Promise<SQLServerDataProvider>;
|
|
5
|
+
export declare function cleanupProvider(): Promise<void>;
|
|
6
|
+
export declare function getSystemUser(): UserInfo;
|
|
7
|
+
/**
|
|
8
|
+
* Recursively find all entity directories with .mj-sync.json files
|
|
9
|
+
* @param dir Directory to search
|
|
10
|
+
* @param specificDir Optional specific directory to limit search to
|
|
11
|
+
* @returns Array of directory paths containing .mj-sync.json files
|
|
12
|
+
*/
|
|
13
|
+
export declare function findEntityDirectories(dir: string, specificDir?: string): string[];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.findEntityDirectories = exports.getSystemUser = exports.cleanupProvider = exports.initializeProvider = void 0;
|
|
27
|
+
const typeorm_1 = require("typeorm");
|
|
28
|
+
const sqlserver_dataprovider_1 = require("@memberjunction/sqlserver-dataprovider");
|
|
29
|
+
const fs = __importStar(require("fs"));
|
|
30
|
+
const path = __importStar(require("path"));
|
|
31
|
+
/**
|
|
32
|
+
* Initialize a SQLServerDataProvider with the given configuration
|
|
33
|
+
* @param config MemberJunction configuration with database connection details
|
|
34
|
+
* @returns Initialized SQLServerDataProvider instance
|
|
35
|
+
*/
|
|
36
|
+
let globalDataSource = null;
|
|
37
|
+
async function initializeProvider(config) {
|
|
38
|
+
// Create TypeORM DataSource
|
|
39
|
+
const dataSource = new typeorm_1.DataSource({
|
|
40
|
+
type: 'mssql',
|
|
41
|
+
host: config.dbHost,
|
|
42
|
+
port: config.dbPort ? Number(config.dbPort) : 1433,
|
|
43
|
+
database: config.dbDatabase,
|
|
44
|
+
username: config.dbUsername,
|
|
45
|
+
password: config.dbPassword,
|
|
46
|
+
synchronize: false,
|
|
47
|
+
logging: false,
|
|
48
|
+
options: {
|
|
49
|
+
encrypt: config.dbTrustServerCertificate !== 'Y' ? false : true,
|
|
50
|
+
trustServerCertificate: config.dbTrustServerCertificate === 'Y',
|
|
51
|
+
instanceName: config.dbInstanceName
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
// Initialize the data source
|
|
55
|
+
await dataSource.initialize();
|
|
56
|
+
// Store for cleanup
|
|
57
|
+
globalDataSource = dataSource;
|
|
58
|
+
// Create provider config
|
|
59
|
+
const providerConfig = new sqlserver_dataprovider_1.SQLServerProviderConfigData(dataSource, 'system@sync.cli', // Default user for CLI
|
|
60
|
+
config.mjCoreSchema || '__mj', 0);
|
|
61
|
+
// Use setupSQLServerClient to properly initialize
|
|
62
|
+
return await (0, sqlserver_dataprovider_1.setupSQLServerClient)(providerConfig);
|
|
63
|
+
}
|
|
64
|
+
exports.initializeProvider = initializeProvider;
|
|
65
|
+
async function cleanupProvider() {
|
|
66
|
+
if (globalDataSource && globalDataSource.isInitialized) {
|
|
67
|
+
await globalDataSource.destroy();
|
|
68
|
+
globalDataSource = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.cleanupProvider = cleanupProvider;
|
|
72
|
+
function getSystemUser() {
|
|
73
|
+
const sysUser = sqlserver_dataprovider_1.UserCache.Instance.UserByName("System", false);
|
|
74
|
+
if (!sysUser) {
|
|
75
|
+
throw new Error("System user not found in cache. Ensure the system user exists in the database.");
|
|
76
|
+
}
|
|
77
|
+
return sysUser;
|
|
78
|
+
}
|
|
79
|
+
exports.getSystemUser = getSystemUser;
|
|
80
|
+
/**
|
|
81
|
+
* Recursively find all entity directories with .mj-sync.json files
|
|
82
|
+
* @param dir Directory to search
|
|
83
|
+
* @param specificDir Optional specific directory to limit search to
|
|
84
|
+
* @returns Array of directory paths containing .mj-sync.json files
|
|
85
|
+
*/
|
|
86
|
+
function findEntityDirectories(dir, specificDir) {
|
|
87
|
+
const results = [];
|
|
88
|
+
function search(currentDir) {
|
|
89
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
90
|
+
// Check if this directory has .mj-sync.json (and it's not the root)
|
|
91
|
+
const hasSyncConfig = entries.some(e => e.name === '.mj-sync.json');
|
|
92
|
+
const isRoot = currentDir === dir;
|
|
93
|
+
if (hasSyncConfig && !isRoot) {
|
|
94
|
+
results.push(currentDir);
|
|
95
|
+
}
|
|
96
|
+
// Recursively search subdirectories
|
|
97
|
+
for (const entry of entries) {
|
|
98
|
+
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
99
|
+
search(path.join(currentDir, entry.name));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// If specific directory is provided, only search within it
|
|
104
|
+
if (specificDir) {
|
|
105
|
+
// Handle both absolute and relative paths
|
|
106
|
+
const targetDir = path.isAbsolute(specificDir) ? specificDir : path.join(dir, specificDir);
|
|
107
|
+
if (fs.existsSync(targetDir)) {
|
|
108
|
+
search(targetDir);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
search(dir);
|
|
113
|
+
}
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
116
|
+
exports.findEntityDirectories = findEntityDirectories;
|
|
117
|
+
//# sourceMappingURL=provider-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-utils.js","sourceRoot":"","sources":["../../src/lib/provider-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { DataSource } from 'typeorm';\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/**\n * Initialize a SQLServerDataProvider with the given configuration\n * @param config MemberJunction configuration with database connection details\n * @returns Initialized SQLServerDataProvider instance\n */\nlet globalDataSource: DataSource | null = null;\n\nexport async function initializeProvider(config: MJConfig): Promise<SQLServerDataProvider> {\n // Create TypeORM DataSource\n const dataSource = new DataSource({\n type: 'mssql',\n host: config.dbHost,\n port: config.dbPort ? Number(config.dbPort) : 1433,\n database: config.dbDatabase,\n username: config.dbUsername,\n password: config.dbPassword,\n synchronize: false,\n logging: false,\n options: {\n encrypt: config.dbTrustServerCertificate !== 'Y' ? false : true,\n trustServerCertificate: config.dbTrustServerCertificate === 'Y',\n instanceName: config.dbInstanceName\n }\n });\n \n // Initialize the data source\n await dataSource.initialize();\n \n // Store for cleanup\n globalDataSource = dataSource;\n \n // Create provider config\n const providerConfig = new SQLServerProviderConfigData(\n dataSource,\n 'system@sync.cli', // Default user for CLI\n config.mjCoreSchema || '__mj',\n 0\n );\n \n // Use setupSQLServerClient to properly initialize\n return await setupSQLServerClient(providerConfig);\n}\n\nexport async function cleanupProvider(): Promise<void> {\n if (globalDataSource && globalDataSource.isInitialized) {\n await globalDataSource.destroy();\n globalDataSource = null;\n }\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 return sysUser;\n}\n\n/**\n * Recursively find all entity directories with .mj-sync.json files\n * @param dir Directory to search\n * @param specificDir Optional specific directory to limit search to\n * @returns Array of directory paths containing .mj-sync.json files\n */\nexport function findEntityDirectories(dir: string, specificDir?: string): string[] {\n const results: string[] = [];\n \n function search(currentDir: string) {\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n \n // Check if this directory has .mj-sync.json (and it's not the root)\n const hasSyncConfig = entries.some(e => e.name === '.mj-sync.json');\n const isRoot = currentDir === dir;\n \n if (hasSyncConfig && !isRoot) {\n results.push(currentDir);\n }\n \n // Recursively search subdirectories\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('.')) {\n search(path.join(currentDir, entry.name));\n }\n }\n }\n \n // If specific directory is provided, only search within it\n if (specificDir) {\n // Handle both absolute and relative paths\n const targetDir = path.isAbsolute(specificDir) ? specificDir : path.join(dir, specificDir);\n if (fs.existsSync(targetDir)) {\n search(targetDir);\n }\n } else {\n search(dir);\n }\n \n return results;\n}"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { EntityInfo, BaseEntity, UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { EntityConfig } from '../config';
|
|
3
|
+
export interface RecordData {
|
|
4
|
+
primaryKey?: Record<string, any>;
|
|
5
|
+
fields: Record<string, any>;
|
|
6
|
+
relatedEntities?: Record<string, RecordData[]>;
|
|
7
|
+
sync?: {
|
|
8
|
+
lastModified: string;
|
|
9
|
+
checksum: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare class SyncEngine {
|
|
13
|
+
private metadata;
|
|
14
|
+
private contextUser;
|
|
15
|
+
constructor(contextUser: UserInfo);
|
|
16
|
+
initialize(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Process special references in field values
|
|
19
|
+
*/
|
|
20
|
+
processFieldValue(value: any, baseDir: string, parentRecord?: BaseEntity | null, rootRecord?: BaseEntity | null): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve a lookup reference to an ID, optionally creating the record if it doesn't exist
|
|
23
|
+
*/
|
|
24
|
+
resolveLookup(entityName: string, fieldName: string, fieldValue: string, autoCreate?: boolean, createFields?: Record<string, any>): Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Build cascading defaults for a file path and process field values
|
|
27
|
+
*/
|
|
28
|
+
buildDefaults(filePath: string, entityConfig: EntityConfig): Promise<Record<string, any>>;
|
|
29
|
+
/**
|
|
30
|
+
* Load folder configuration
|
|
31
|
+
*/
|
|
32
|
+
private loadFolderConfig;
|
|
33
|
+
/**
|
|
34
|
+
* Calculate checksum for data
|
|
35
|
+
*/
|
|
36
|
+
calculateChecksum(data: any): string;
|
|
37
|
+
/**
|
|
38
|
+
* Get entity info by name
|
|
39
|
+
*/
|
|
40
|
+
getEntityInfo(entityName: string): EntityInfo | null;
|
|
41
|
+
/**
|
|
42
|
+
* Create a new entity object
|
|
43
|
+
*/
|
|
44
|
+
createEntityObject(entityName: string): Promise<BaseEntity>;
|
|
45
|
+
/**
|
|
46
|
+
* Load an entity by primary key
|
|
47
|
+
*/
|
|
48
|
+
loadEntity(entityName: string, primaryKey: Record<string, any>): Promise<BaseEntity | null>;
|
|
49
|
+
}
|