@memberjunction/metadata-sync 2.48.0 → 2.50.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 +597 -0
- package/dist/commands/init/index.js +25 -0
- package/dist/commands/init/index.js.map +1 -1
- package/dist/commands/pull/index.d.ts +7 -0
- package/dist/commands/pull/index.js +87 -5
- package/dist/commands/pull/index.js.map +1 -1
- package/dist/commands/push/index.js +165 -4
- package/dist/commands/push/index.js.map +1 -1
- package/dist/commands/status/index.js +31 -0
- package/dist/commands/status/index.js.map +1 -1
- package/dist/commands/watch/index.js +40 -1
- package/dist/commands/watch/index.js.map +1 -1
- package/dist/config.d.ts +27 -0
- package/dist/config.js.map +1 -1
- package/dist/lib/provider-utils.d.ts +26 -5
- package/dist/lib/provider-utils.js +95 -30
- package/dist/lib/provider-utils.js.map +1 -1
- package/dist/lib/sync-engine.js +16 -1
- package/dist/lib/sync-engine.js.map +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -98,6 +98,386 @@ The tool is intended for managing business-level metadata such as:
|
|
|
98
98
|
|
|
99
99
|
For more information about how CodeGen reflects system-level data from the database into the MJ metadata layer, see the [CodeGen documentation](../CodeGen/README.md).
|
|
100
100
|
|
|
101
|
+
## Creating Error-Free Entity Files
|
|
102
|
+
|
|
103
|
+
### Quick Start Checklist
|
|
104
|
+
|
|
105
|
+
Before creating entity JSON files, follow this checklist to avoid common mistakes:
|
|
106
|
+
|
|
107
|
+
✅ **1. Find the Entity Definition**
|
|
108
|
+
- Open `packages/MJCoreEntities/src/generated/entity_subclasses.ts` or `packages/GeneratedEntities/src/generated/entity_subclasses.ts`
|
|
109
|
+
- Search for `class [EntityName]Entity` (e.g., `class TemplateEntity`)
|
|
110
|
+
- Review JSDoc comments and property definitions to identify required vs optional fields
|
|
111
|
+
|
|
112
|
+
✅ **2. Check Required Fields**
|
|
113
|
+
- Look for JSDoc comments with `@required` annotations
|
|
114
|
+
- Fields without `?` in TypeScript definitions are typically required
|
|
115
|
+
- Always include `Name` (almost always required)
|
|
116
|
+
- Always include `UserID` (use System User ID: `ECAFCCEC-6A37-EF11-86D4-000D3A4E707E`)
|
|
117
|
+
|
|
118
|
+
✅ **3. Validate Field Names**
|
|
119
|
+
- Use exact field names from the BaseEntity class definition
|
|
120
|
+
- Field names are case-sensitive
|
|
121
|
+
- Don't assume fields exist (e.g., not all entities have `Status`)
|
|
122
|
+
|
|
123
|
+
✅ **4. Use Correct File Naming**
|
|
124
|
+
- Configuration files (.mj-sync.json, .mj-folder.json) must start with dot
|
|
125
|
+
- Metadata files follow the `filePattern` in your .mj-sync.json
|
|
126
|
+
- Most common: `"filePattern": "*.json"` (matches any .json file)
|
|
127
|
+
- Alternative: `"filePattern": ".*.json"` (matches dot-prefixed .json files)
|
|
128
|
+
|
|
129
|
+
✅ **5. Set Up Directory Structure**
|
|
130
|
+
- Create `.mj-sync.json` in the entity directory
|
|
131
|
+
- Use glob patterns: `"filePattern": "*.json"` (not regex: `".*.json"`)
|
|
132
|
+
|
|
133
|
+
### Discovering Entity Structure
|
|
134
|
+
|
|
135
|
+
**CRITICAL**: Before creating entity files, you must understand the entity's field structure. Most errors occur because users are unfamiliar with the required fields, data types, and constraints.
|
|
136
|
+
|
|
137
|
+
#### Finding Entity Definitions
|
|
138
|
+
|
|
139
|
+
The approach depends on whether you're working inside or outside the MemberJunction monorepo:
|
|
140
|
+
|
|
141
|
+
##### Working Inside MJ Monorepo
|
|
142
|
+
|
|
143
|
+
Entity classes are located in:
|
|
144
|
+
|
|
145
|
+
- **Core MJ Entities**: `packages/MJCoreEntities/src/generated/entity_subclasses.ts`
|
|
146
|
+
- System entities like Users, Roles, EntityFields, etc.
|
|
147
|
+
- AI-related entities like AI Prompts, AI Models, etc.
|
|
148
|
+
|
|
149
|
+
- **Custom Entities**: `packages/GeneratedEntities/src/generated/entity_subclasses.ts`
|
|
150
|
+
- Your application-specific entities
|
|
151
|
+
- Business domain entities
|
|
152
|
+
|
|
153
|
+
##### Working Outside MJ Monorepo (In Your Own Project)
|
|
154
|
+
|
|
155
|
+
Entity classes are located in:
|
|
156
|
+
|
|
157
|
+
- **Core MJ Entities**: `node_modules/@memberjunction/core-entities/dist/generated/entity_subclasses.js`
|
|
158
|
+
- Note: This is compiled JavaScript, but your IDE should provide IntelliSense
|
|
159
|
+
- For TypeScript definitions: `node_modules/@memberjunction/core-entities/dist/generated/entity_subclasses.d.ts`
|
|
160
|
+
|
|
161
|
+
- **Custom Entities**: Your project's generated entities location (varies by project structure)
|
|
162
|
+
- Common locations: `src/generated/`, `packages/entities/`, or similar
|
|
163
|
+
- Look for files containing your custom entity classes
|
|
164
|
+
|
|
165
|
+
##### Best Practice: Use Your IDE's IntelliSense
|
|
166
|
+
|
|
167
|
+
**Recommended approach for all scenarios:**
|
|
168
|
+
|
|
169
|
+
1. **Import the entity class** in your IDE:
|
|
170
|
+
```typescript
|
|
171
|
+
import { TemplateEntity } from '@memberjunction/core-entities';
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
2. **Create an instance and explore with IntelliSense**:
|
|
175
|
+
```typescript
|
|
176
|
+
const template = new TemplateEntity();
|
|
177
|
+
// Type "template." and let your IDE show available properties
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
3. **Check the class definition** (F12 or "Go to Definition") to see:
|
|
181
|
+
- JSDoc comments with field descriptions
|
|
182
|
+
- Required vs optional fields
|
|
183
|
+
- Field types and validation rules
|
|
184
|
+
- Relationships and constraints
|
|
185
|
+
|
|
186
|
+
#### How to Find Required Fields
|
|
187
|
+
|
|
188
|
+
1. **Use IDE IntelliSense** (Recommended):
|
|
189
|
+
- Import the entity class
|
|
190
|
+
- Create an instance: `const entity = new TemplateEntity();`
|
|
191
|
+
- Use "Go to Definition" (F12) to see the BaseEntity class
|
|
192
|
+
- Look for JSDoc comments and field definitions
|
|
193
|
+
|
|
194
|
+
2. **Examine the BaseEntity Class**:
|
|
195
|
+
- Find the entity class (e.g., `class TemplateEntity`)
|
|
196
|
+
- Look at property declarations with JSDoc comments
|
|
197
|
+
- Check for required vs optional field annotations
|
|
198
|
+
- Review any validation methods or constraints
|
|
199
|
+
|
|
200
|
+
3. **Runtime Metadata Discovery**:
|
|
201
|
+
```typescript
|
|
202
|
+
import { Metadata } from '@memberjunction/core';
|
|
203
|
+
|
|
204
|
+
const md = new Metadata();
|
|
205
|
+
const entityInfo = md.EntityByName('Templates');
|
|
206
|
+
console.log('Required fields:', entityInfo.Fields.filter(f => !f.AllowsNull));
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### Example: Templates Entity Structure
|
|
210
|
+
```typescript
|
|
211
|
+
// BaseEntity class (accessible via IDE IntelliSense)
|
|
212
|
+
export class TemplateEntity extends BaseEntity {
|
|
213
|
+
/**
|
|
214
|
+
* Primary key - auto-generated GUID
|
|
215
|
+
*/
|
|
216
|
+
ID: string;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Template name - REQUIRED
|
|
220
|
+
* @required
|
|
221
|
+
*/
|
|
222
|
+
Name: string;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Template description - optional
|
|
226
|
+
*/
|
|
227
|
+
Description?: string;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* User who created this template - REQUIRED
|
|
231
|
+
* Must be a valid User ID
|
|
232
|
+
* @required
|
|
233
|
+
* @foreignKey Users.ID
|
|
234
|
+
*/
|
|
235
|
+
UserID: string;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Category for organizing templates - optional
|
|
239
|
+
* @foreignKey TemplateCategories.ID
|
|
240
|
+
*/
|
|
241
|
+
CategoryID?: string;
|
|
242
|
+
|
|
243
|
+
// Note: Status field may not exist on all entities!
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Common Required Fields Pattern
|
|
248
|
+
|
|
249
|
+
Most MJ entities follow these patterns:
|
|
250
|
+
|
|
251
|
+
**Always Required:**
|
|
252
|
+
- `ID` - Primary key (GUID) - auto-generated if not provided
|
|
253
|
+
- `Name` - Human-readable name
|
|
254
|
+
- `UserID` - Creator/owner (use System User: `ECAFCCEC-6A37-EF11-86D4-000D3A4E707E`)
|
|
255
|
+
|
|
256
|
+
**Often Required:**
|
|
257
|
+
- `Description` - Usually optional but recommended
|
|
258
|
+
- Foreign key fields ending in `ID` - Check if they have `.optional()`
|
|
259
|
+
|
|
260
|
+
**Be Careful With:**
|
|
261
|
+
- `Status` fields - Some entities have them, others don't
|
|
262
|
+
- Enum fields - Must match exact values from database
|
|
263
|
+
- DateTime fields - Use ISO format: `2024-01-15T10:30:00Z`
|
|
264
|
+
|
|
265
|
+
### Common Mistakes and Solutions
|
|
266
|
+
|
|
267
|
+
#### ❌ Mistake 1: Using Non-Existent Fields
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"fields": {
|
|
271
|
+
"Name": "My Template",
|
|
272
|
+
"Status": "Active" // ❌ Templates entity may not have Status field
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**✅ Solution**: Check the BaseEntity class first
|
|
278
|
+
```typescript
|
|
279
|
+
// In entity_subclasses.ts - if you don't see Status here, don't use it
|
|
280
|
+
export class TemplateEntity extends BaseEntity {
|
|
281
|
+
Name: string; // Required
|
|
282
|
+
Description?: string; // Optional (note the ?)
|
|
283
|
+
// No Status field defined
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### ❌ Mistake 2: Missing Required Fields
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"fields": {
|
|
291
|
+
"Name": "My Template"
|
|
292
|
+
// ❌ Missing required UserID
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**✅ Solution**: Include all required fields
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"fields": {
|
|
301
|
+
"Name": "My Template",
|
|
302
|
+
"UserID": "ECAFCCEC-6A37-EF11-86D4-000D3A4E707E"
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### ❌ Mistake 3: Wrong File Pattern in .mj-sync.json
|
|
308
|
+
```json
|
|
309
|
+
{
|
|
310
|
+
"entity": "Templates",
|
|
311
|
+
"filePattern": ".*.json" // ❌ This is regex, not glob
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**✅ Solution**: Use glob patterns
|
|
316
|
+
```json
|
|
317
|
+
{
|
|
318
|
+
"entity": "Templates",
|
|
319
|
+
"filePattern": "*.json" // ✅ Correct glob pattern
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
#### ❌ Mistake 4: Incorrect Data Types
|
|
324
|
+
```json
|
|
325
|
+
{
|
|
326
|
+
"fields": {
|
|
327
|
+
"Name": "My Template",
|
|
328
|
+
"CreatedAt": "2024-01-15", // ❌ Wrong datetime format
|
|
329
|
+
"Priority": "1" // ❌ Should be number, not string
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**✅ Solution**: Use correct data types
|
|
335
|
+
```json
|
|
336
|
+
{
|
|
337
|
+
"fields": {
|
|
338
|
+
"Name": "My Template",
|
|
339
|
+
"CreatedAt": "2024-01-15T10:30:00Z", // ✅ ISO format
|
|
340
|
+
"Priority": 1 // ✅ Number type
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### ❌ Mistake 5: Files Not Being Detected
|
|
346
|
+
```
|
|
347
|
+
mydir/
|
|
348
|
+
├── .mj-sync.json (with "filePattern": "*.json")
|
|
349
|
+
├── template1.txt // ❌ Wrong extension
|
|
350
|
+
└── .template2.json // ❌ Dot prefix when pattern is "*.json"
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**✅ Solution**: Match your filePattern
|
|
354
|
+
```
|
|
355
|
+
mydir/
|
|
356
|
+
├── .mj-sync.json (with "filePattern": "*.json")
|
|
357
|
+
├── template1.json // ✅ Matches *.json pattern
|
|
358
|
+
└── template2.json // ✅ Matches *.json pattern
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Step-by-Step Entity File Creation
|
|
362
|
+
|
|
363
|
+
#### Step 1: Research the Entity
|
|
364
|
+
```bash
|
|
365
|
+
# Open in your IDE:
|
|
366
|
+
packages/MJCoreEntities/src/generated/entity_subclasses.ts
|
|
367
|
+
|
|
368
|
+
# Search for your entity class (Ctrl+F):
|
|
369
|
+
class TemplateEntity
|
|
370
|
+
|
|
371
|
+
# Note the required vs optional fields:
|
|
372
|
+
Name: string; // Required (no ?)
|
|
373
|
+
UserID: string; // Required (no ?)
|
|
374
|
+
Description?: string; // Optional (note the ?)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
#### Step 2: Create Directory Structure
|
|
378
|
+
```bash
|
|
379
|
+
mkdir templates
|
|
380
|
+
cd templates
|
|
381
|
+
|
|
382
|
+
# Create entity config (dot-prefixed configuration file)
|
|
383
|
+
echo '{
|
|
384
|
+
"entity": "Templates",
|
|
385
|
+
"filePattern": "*.json"
|
|
386
|
+
}' > .mj-sync.json
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### Step 3: Create Your First Entity File
|
|
390
|
+
```bash
|
|
391
|
+
# Create metadata file (follows filePattern from .mj-sync.json)
|
|
392
|
+
echo '{
|
|
393
|
+
"fields": {
|
|
394
|
+
"Name": "My First Template",
|
|
395
|
+
"Description": "A test template",
|
|
396
|
+
"UserID": "ECAFCCEC-6A37-EF11-86D4-000D3A4E707E"
|
|
397
|
+
}
|
|
398
|
+
}' > my-first-template.json
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
#### Step 4: Test and Validate
|
|
402
|
+
```bash
|
|
403
|
+
# Dry run to check for errors
|
|
404
|
+
mj-sync push --dir="templates" --dry-run
|
|
405
|
+
|
|
406
|
+
# If successful, do actual push
|
|
407
|
+
mj-sync push --dir="templates"
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### AI/LLM Guidelines
|
|
411
|
+
|
|
412
|
+
When using AI tools (like Claude, ChatGPT, etc.) to generate entity files:
|
|
413
|
+
|
|
414
|
+
**🤖 For AI Assistants:**
|
|
415
|
+
|
|
416
|
+
1. **Always check entity definitions first** - Never assume field names or requirements
|
|
417
|
+
2. **Look up the exact BaseEntity class** in the generated entity files
|
|
418
|
+
3. **Use the System User ID** (`ECAFCCEC-6A37-EF11-86D4-000D3A4E707E`) for UserID fields
|
|
419
|
+
4. **Include only fields that exist** in the entity definition
|
|
420
|
+
5. **Use proper data types** as defined in the BaseEntity class
|
|
421
|
+
6. **Remember file naming rules**:
|
|
422
|
+
- Configuration files (.mj-sync.json) must have dot prefix
|
|
423
|
+
- Metadata files follow the filePattern in .mj-sync.json
|
|
424
|
+
7. **Use glob patterns** in .mj-sync.json, not regex patterns
|
|
425
|
+
|
|
426
|
+
**📝 Prompt Template for AI:**
|
|
427
|
+
```
|
|
428
|
+
I need to create entity files for the [EntityName] entity in MemberJunction.
|
|
429
|
+
|
|
430
|
+
Please:
|
|
431
|
+
1. First, check the entity definition in packages/MJCoreEntities/src/generated/entity_subclasses.ts
|
|
432
|
+
2. Find the class [EntityName]Entity (e.g., class TemplateEntity)
|
|
433
|
+
3. Review JSDoc comments and property definitions to identify required vs optional fields
|
|
434
|
+
4. Create a .mj-sync.json file with correct glob pattern
|
|
435
|
+
5. Create sample metadata JSON files following the filePattern
|
|
436
|
+
6. Use UserID: "ECAFCCEC-6A37-EF11-86D4-000D3A4E707E" for required UserID fields
|
|
437
|
+
7. Follow the exact field names and data types from the BaseEntity class definition
|
|
438
|
+
|
|
439
|
+
CRITICAL: Configuration files (.mj-sync.json) must start with dot, but metadata files follow the filePattern specified in the configuration.
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Understanding File Naming Rules
|
|
443
|
+
|
|
444
|
+
**Configuration Files (Always Dot-Prefixed):**
|
|
445
|
+
- ✅ `.mj-sync.json` - Entity configuration
|
|
446
|
+
- ✅ `.mj-folder.json` - Folder defaults
|
|
447
|
+
- ❌ `mj-sync.json` - Won't be recognized
|
|
448
|
+
|
|
449
|
+
**Metadata Files (Follow filePattern):**
|
|
450
|
+
With `"filePattern": "*.json"`:
|
|
451
|
+
- ✅ `my-template.json` - Will be processed
|
|
452
|
+
- ✅ `greeting.json` - Will be processed
|
|
453
|
+
- ❌ `.my-template.json` - Won't match pattern
|
|
454
|
+
- ❌ `package.json` - Will be ignored (add to ignore list if needed)
|
|
455
|
+
|
|
456
|
+
With `"filePattern": ".*.json"`:
|
|
457
|
+
- ✅ `.my-template.json` - Will be processed
|
|
458
|
+
- ✅ `.greeting.json` - Will be processed
|
|
459
|
+
- ❌ `my-template.json` - Won't match pattern
|
|
460
|
+
- ❌ `package.json` - Won't match pattern
|
|
461
|
+
|
|
462
|
+
### Troubleshooting Quick Reference
|
|
463
|
+
|
|
464
|
+
| Error Message | Cause | Solution |
|
|
465
|
+
|---------------|-------|----------|
|
|
466
|
+
| `No entity directories found` | Missing .mj-sync.json or wrong filePattern | Check .mj-sync.json exists and uses `"*.json"` |
|
|
467
|
+
| `Field 'X' does not exist on entity 'Y'` | Using non-existent field | Check BaseEntity class in entity_subclasses.ts |
|
|
468
|
+
| `User ID cannot be null` | Missing required UserID | Add `"UserID": "ECAFCCEC-6A37-EF11-86D4-000D3A4E707E"` |
|
|
469
|
+
| `Processing 0 records` | Files don't match filePattern | Check files match pattern in .mj-sync.json |
|
|
470
|
+
| Failed validation | Wrong data type or format | Check BaseEntity class for field types |
|
|
471
|
+
|
|
472
|
+
### System User ID Reference
|
|
473
|
+
|
|
474
|
+
**Always use this GUID for UserID fields:**
|
|
475
|
+
```
|
|
476
|
+
ECAFCCEC-6A37-EF11-86D4-000D3A4E707E
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
This is the System User ID that should be used when creating entity records through the MetadataSync tool. Using any other ID or leaving it null will cause validation errors.
|
|
480
|
+
|
|
101
481
|
## File Structure
|
|
102
482
|
|
|
103
483
|
The tool uses a hierarchical directory structure with cascading defaults:
|
|
@@ -471,14 +851,129 @@ Configuration follows a hierarchical structure:
|
|
|
471
851
|
- **Entity configs**: Each entity directory has its own config defining the entity type
|
|
472
852
|
- **Inheritance**: All files within an entity directory are treated as records of that entity type
|
|
473
853
|
|
|
854
|
+
### Directory Processing Order (NEW)
|
|
855
|
+
|
|
856
|
+
The MetadataSync tool now supports custom directory processing order to handle dependencies between entity types. This feature ensures that dependent entities are processed in the correct order.
|
|
857
|
+
|
|
858
|
+
#### Directory Order Configuration
|
|
859
|
+
|
|
860
|
+
Directory order is configured in the root-level `.mj-sync.json` file only (not inherited by subdirectories):
|
|
861
|
+
|
|
862
|
+
```json
|
|
863
|
+
{
|
|
864
|
+
"version": "1.0.0",
|
|
865
|
+
"directoryOrder": [
|
|
866
|
+
"prompts",
|
|
867
|
+
"agent-types"
|
|
868
|
+
]
|
|
869
|
+
}
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
#### How It Works
|
|
873
|
+
|
|
874
|
+
- **Ordered Processing**: Directories listed in `directoryOrder` are processed first, in the specified order
|
|
875
|
+
- **Remaining Directories**: Any directories not listed are processed after the ordered ones, in alphabetical order
|
|
876
|
+
- **Dependency Management**: Ensures prompts are created before agent types that reference them
|
|
877
|
+
- **Flexible**: Only specify the directories that have order requirements
|
|
878
|
+
|
|
879
|
+
#### Example Use Cases
|
|
880
|
+
|
|
881
|
+
1. **AI Prompts → Agent Types**: Create prompts before agent types that reference them
|
|
882
|
+
2. **Categories → Items**: Create category records before items that reference them
|
|
883
|
+
3. **Parent → Child**: Process parent entities before child entities with foreign key dependencies
|
|
884
|
+
|
|
885
|
+
### SQL Logging (NEW)
|
|
886
|
+
|
|
887
|
+
The MetadataSync tool now supports SQL logging for capturing all database operations during push commands. This feature is useful for:
|
|
888
|
+
- Creating migration files from MetadataSync operations
|
|
889
|
+
- Debugging database changes
|
|
890
|
+
- Understanding what SQL operations occur during push
|
|
891
|
+
- Creating migration scripts for deployment to other environments
|
|
892
|
+
|
|
893
|
+
#### SQL Logging Configuration
|
|
894
|
+
|
|
895
|
+
SQL logging is configured in the root-level `.mj-sync.json` file only (not inherited by subdirectories):
|
|
896
|
+
|
|
897
|
+
```json
|
|
898
|
+
{
|
|
899
|
+
"version": "1.0.0",
|
|
900
|
+
"sqlLogging": {
|
|
901
|
+
"enabled": true,
|
|
902
|
+
"outputDirectory": "./sql_logging",
|
|
903
|
+
"formatAsMigration": true
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
#### SQL Logging Options
|
|
909
|
+
|
|
910
|
+
| Option | Type | Default | Description |
|
|
911
|
+
|--------|------|---------|-------------|
|
|
912
|
+
| `enabled` | boolean | false | Whether to enable SQL logging during push operations |
|
|
913
|
+
| `outputDirectory` | string | "./sql_logging" | Directory to output SQL log files (relative to command execution directory) |
|
|
914
|
+
| `formatAsMigration` | boolean | false | Whether to format SQL as migration-ready files with Flyway schema placeholders |
|
|
915
|
+
|
|
916
|
+
#### SQL Log File Format
|
|
917
|
+
|
|
918
|
+
When `formatAsMigration` is `false`, log files are named:
|
|
919
|
+
```
|
|
920
|
+
metadatasync-push-YYYY-MM-DDTHH-MM-SS.sql
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
When `formatAsMigration` is `true`, log files are named as Flyway migrations:
|
|
924
|
+
```
|
|
925
|
+
VYYYYMMDDHHMMSS__MetadataSync_Push.sql
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
Migration files include:
|
|
929
|
+
- Header comments with timestamp and description
|
|
930
|
+
- Schema placeholders that can be replaced during deployment
|
|
931
|
+
- Properly formatted SQL statements with parameters
|
|
932
|
+
|
|
933
|
+
#### Example Usage
|
|
934
|
+
|
|
935
|
+
1. **Enable SQL logging** in your root `.mj-sync.json`:
|
|
936
|
+
```json
|
|
937
|
+
{
|
|
938
|
+
"version": "1.0.0",
|
|
939
|
+
"sqlLogging": {
|
|
940
|
+
"enabled": true,
|
|
941
|
+
"outputDirectory": "./migrations",
|
|
942
|
+
"formatAsMigration": true
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
2. **Run push command** as normal:
|
|
948
|
+
```bash
|
|
949
|
+
mj-sync push
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
3. **Review generated SQL** in the output directory:
|
|
953
|
+
```
|
|
954
|
+
migrations/
|
|
955
|
+
└── V20241215103045__MetadataSync_Push.sql
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
The SQL logging runs in parallel with the actual database operations, ensuring minimal performance impact while capturing all SQL statements for review and potential migration use.
|
|
959
|
+
|
|
474
960
|
### Root Configuration (metadata/.mj-sync.json)
|
|
475
961
|
```json
|
|
476
962
|
{
|
|
477
963
|
"version": "1.0.0",
|
|
964
|
+
"directoryOrder": [
|
|
965
|
+
"prompts",
|
|
966
|
+
"agent-types"
|
|
967
|
+
],
|
|
478
968
|
"push": {
|
|
479
969
|
"validateBeforePush": true,
|
|
480
970
|
"requireConfirmation": true
|
|
481
971
|
},
|
|
972
|
+
"sqlLogging": {
|
|
973
|
+
"enabled": true,
|
|
974
|
+
"outputDirectory": "./sql_logging",
|
|
975
|
+
"formatAsMigration": false
|
|
976
|
+
},
|
|
482
977
|
"watch": {
|
|
483
978
|
"debounceMs": 1000,
|
|
484
979
|
"ignorePatterns": ["*.tmp", "*.bak"]
|
|
@@ -552,6 +1047,108 @@ The tool now supports managing related entities as embedded collections within p
|
|
|
552
1047
|
- **Cleaner Organization**: Fewer files to manage
|
|
553
1048
|
- **Relationship Clarity**: Visual representation of data relationships
|
|
554
1049
|
|
|
1050
|
+
## Recursive Patterns (NEW)
|
|
1051
|
+
|
|
1052
|
+
The tool now supports automatic recursive patterns for self-referencing entities, eliminating the need to manually define each nesting level for hierarchical data structures.
|
|
1053
|
+
|
|
1054
|
+
### Benefits
|
|
1055
|
+
- **Simplified Configuration**: No need to manually define each hierarchy level
|
|
1056
|
+
- **Automatic Depth Handling**: Adapts to actual data depth dynamically
|
|
1057
|
+
- **Reduced Maintenance**: Configuration stays simple regardless of data changes
|
|
1058
|
+
- **Safeguards**: Built-in protection against infinite loops and excessive memory usage
|
|
1059
|
+
|
|
1060
|
+
### Recursive Configuration
|
|
1061
|
+
|
|
1062
|
+
Enable recursive patterns for self-referencing entities:
|
|
1063
|
+
|
|
1064
|
+
```json
|
|
1065
|
+
{
|
|
1066
|
+
"pull": {
|
|
1067
|
+
"entities": {
|
|
1068
|
+
"AI Agents": {
|
|
1069
|
+
"relatedEntities": {
|
|
1070
|
+
"AI Agents": {
|
|
1071
|
+
"entity": "AI Agents",
|
|
1072
|
+
"foreignKey": "ParentID",
|
|
1073
|
+
"recursive": true, // Enable recursive fetching
|
|
1074
|
+
"maxDepth": 10, // Optional depth limit (omit for default of 10)
|
|
1075
|
+
"filter": "Status = 'Active'"
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
### How It Works
|
|
1085
|
+
|
|
1086
|
+
When `recursive: true` is set:
|
|
1087
|
+
|
|
1088
|
+
1. **Automatic Child Fetching**: The tool automatically fetches child records at each level
|
|
1089
|
+
2. **Dynamic Depth**: Continues until no more children are found or max depth is reached
|
|
1090
|
+
3. **Circular Reference Protection**: Prevents infinite loops by tracking processed record IDs
|
|
1091
|
+
4. **Consistent Configuration**: All recursive levels use the same `lookupFields`, `externalizeFields`, etc.
|
|
1092
|
+
|
|
1093
|
+
### Before vs After
|
|
1094
|
+
|
|
1095
|
+
**Before (Manual Configuration):**
|
|
1096
|
+
```json
|
|
1097
|
+
{
|
|
1098
|
+
"pull": {
|
|
1099
|
+
"relatedEntities": {
|
|
1100
|
+
"AI Agents": {
|
|
1101
|
+
"entity": "AI Agents",
|
|
1102
|
+
"foreignKey": "ParentID",
|
|
1103
|
+
"relatedEntities": {
|
|
1104
|
+
"AI Agents": {
|
|
1105
|
+
"entity": "AI Agents",
|
|
1106
|
+
"foreignKey": "ParentID",
|
|
1107
|
+
"relatedEntities": {
|
|
1108
|
+
"AI Agents": {
|
|
1109
|
+
"entity": "AI Agents",
|
|
1110
|
+
"foreignKey": "ParentID"
|
|
1111
|
+
// Must manually add more levels...
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
**After (Recursive Configuration):**
|
|
1123
|
+
```json
|
|
1124
|
+
{
|
|
1125
|
+
"pull": {
|
|
1126
|
+
"relatedEntities": {
|
|
1127
|
+
"AI Agents": {
|
|
1128
|
+
"entity": "AI Agents",
|
|
1129
|
+
"foreignKey": "ParentID",
|
|
1130
|
+
"recursive": true,
|
|
1131
|
+
"maxDepth": 10
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
### Configuration Options
|
|
1139
|
+
|
|
1140
|
+
| Option | Type | Default | Description |
|
|
1141
|
+
|--------|------|---------|-------------|
|
|
1142
|
+
| `recursive` | boolean | false | Enable automatic recursive fetching |
|
|
1143
|
+
| `maxDepth` | number | 10 | Maximum recursion depth to prevent infinite loops |
|
|
1144
|
+
|
|
1145
|
+
### Safeguards
|
|
1146
|
+
|
|
1147
|
+
- **Circular Reference Detection**: Tracks processed record IDs to prevent infinite loops
|
|
1148
|
+
- **Maximum Depth Limit**: Configurable depth limit (default: 10) prevents excessive memory usage
|
|
1149
|
+
- **Performance Monitoring**: Verbose mode shows recursion depth and skipped circular references
|
|
1150
|
+
- **Backward Compatibility**: Existing configurations continue to work unchanged
|
|
1151
|
+
|
|
555
1152
|
### Configuration for Pull
|
|
556
1153
|
|
|
557
1154
|
The pull command now supports smart update capabilities with extensive configuration options:
|
|
@@ -90,6 +90,31 @@ class Init extends core_1.Command {
|
|
|
90
90
|
}
|
|
91
91
|
catch (error) {
|
|
92
92
|
spinner.fail('Initialization failed');
|
|
93
|
+
// Enhanced error logging for debugging
|
|
94
|
+
this.log('\n=== Initialization Error Details ===');
|
|
95
|
+
this.log(`Error type: ${error?.constructor?.name || 'Unknown'}`);
|
|
96
|
+
this.log(`Error message: ${error instanceof Error ? error.message : String(error)}`);
|
|
97
|
+
if (error instanceof Error && error.stack) {
|
|
98
|
+
this.log(`\nStack trace:`);
|
|
99
|
+
this.log(error.stack);
|
|
100
|
+
}
|
|
101
|
+
// Log context information
|
|
102
|
+
this.log(`\nContext:`);
|
|
103
|
+
this.log(`- Working directory: ${process.cwd()}`);
|
|
104
|
+
// Check if error is related to common issues
|
|
105
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
106
|
+
if (errorMessage.includes('permission') || errorMessage.includes('EACCES')) {
|
|
107
|
+
this.log(`\nHint: This appears to be a file permission issue.`);
|
|
108
|
+
this.log(`Make sure you have write permissions in the current directory.`);
|
|
109
|
+
}
|
|
110
|
+
else if (errorMessage.includes('ENOENT') || errorMessage.includes('no such file')) {
|
|
111
|
+
this.log(`\nHint: This appears to be a file or directory access issue.`);
|
|
112
|
+
this.log(`Make sure the current directory exists and is accessible.`);
|
|
113
|
+
}
|
|
114
|
+
else if (errorMessage.includes('already exists') || errorMessage.includes('EEXIST')) {
|
|
115
|
+
this.log(`\nHint: Files or directories already exist.`);
|
|
116
|
+
this.log(`Try using the overwrite option or manually remove existing files.`);
|
|
117
|
+
}
|
|
93
118
|
this.error(error);
|
|
94
119
|
}
|
|
95
120
|
}
|