@memberjunction/metadata-sync 2.47.0 → 2.49.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 +814 -15
- package/dist/commands/init/index.js +25 -0
- package/dist/commands/init/index.js.map +1 -1
- package/dist/commands/pull/index.d.ts +97 -5
- package/dist/commands/pull/index.js +1009 -115
- package/dist/commands/pull/index.js.map +1 -1
- package/dist/commands/push/index.js +181 -12
- package/dist/commands/push/index.js.map +1 -1
- package/dist/commands/status/index.js +49 -7
- package/dist/commands/status/index.js.map +1 -1
- package/dist/commands/watch/index.js +59 -7
- package/dist/commands/watch/index.js.map +1 -1
- package/dist/config.d.ts +96 -0
- package/dist/config.js +2 -13
- package/dist/config.js.map +1 -1
- package/dist/hooks/init.js +3 -0
- package/dist/hooks/init.js.map +1 -1
- package/dist/lib/config-manager.d.ts +56 -0
- package/dist/lib/config-manager.js +104 -0
- package/dist/lib/config-manager.js.map +1 -0
- package/dist/lib/provider-utils.d.ts +26 -5
- package/dist/lib/provider-utils.js +127 -43
- package/dist/lib/provider-utils.js.map +1 -1
- package/dist/lib/singleton-manager.d.ts +34 -0
- package/dist/lib/singleton-manager.js +62 -0
- package/dist/lib/singleton-manager.js.map +1 -0
- package/dist/lib/sync-engine.js +16 -1
- package/dist/lib/sync-engine.js.map +1 -1
- package/oclif.manifest.json +50 -43
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -98,16 +98,410 @@ 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:
|
|
104
484
|
- Each top-level directory represents an entity type
|
|
105
485
|
- `.mj-sync.json` files define entities and base defaults
|
|
106
486
|
- `.mj-folder.json` files define folder-specific defaults (optional)
|
|
107
|
-
-
|
|
487
|
+
- Only dot-prefixed JSON files (e.g., `.prompt-template.json`, `.category.json`) are treated as metadata records
|
|
488
|
+
- Regular JSON files without the dot prefix are ignored, allowing package.json and other config files to coexist
|
|
108
489
|
- External files (`.md`, `.html`, etc.) are referenced from the JSON files
|
|
109
490
|
- Defaults cascade down through the folder hierarchy
|
|
110
491
|
|
|
492
|
+
### File Naming Convention
|
|
493
|
+
|
|
494
|
+
**Metadata files must be prefixed with a dot (.)** to be recognized by the sync tool. This convention:
|
|
495
|
+
- Clearly distinguishes metadata files from regular configuration files
|
|
496
|
+
- Allows `package.json`, `tsconfig.json` and other standard files to coexist without being processed
|
|
497
|
+
- Follows established patterns like `.gitignore` and `.eslintrc.json`
|
|
498
|
+
|
|
499
|
+
Examples:
|
|
500
|
+
- ✅ `.greeting.json` - Will be processed as metadata
|
|
501
|
+
- ✅ `.customer-prompt.json` - Will be processed as metadata
|
|
502
|
+
- ❌ `greeting.json` - Will be ignored
|
|
503
|
+
- ❌ `package.json` - Will be ignored
|
|
504
|
+
|
|
111
505
|
### File Format Options
|
|
112
506
|
|
|
113
507
|
#### Single Record per File (Default)
|
|
@@ -147,12 +541,12 @@ metadata/
|
|
|
147
541
|
│ ├── .mj-sync.json # Defines entity: "AI Prompts"
|
|
148
542
|
│ ├── customer-service/
|
|
149
543
|
│ │ ├── .mj-folder.json # Folder metadata (CategoryID, etc.)
|
|
150
|
-
│ │ ├── greeting.json
|
|
544
|
+
│ │ ├── .greeting.json # AI Prompt record with embedded models
|
|
151
545
|
│ │ ├── greeting.prompt.md # Prompt content (referenced)
|
|
152
546
|
│ │ └── greeting.notes.md # Notes field (referenced)
|
|
153
547
|
│ └── analytics/
|
|
154
548
|
│ ├── .mj-folder.json # Folder metadata (CategoryID, etc.)
|
|
155
|
-
│ ├── daily-report.json
|
|
549
|
+
│ ├── .daily-report.json # AI Prompt record
|
|
156
550
|
│ └── daily-report.prompt.md # Prompt content (referenced)
|
|
157
551
|
├── templates/ # Reusable JSON templates
|
|
158
552
|
│ ├── standard-prompt-settings.json # Common prompt configurations
|
|
@@ -163,17 +557,17 @@ metadata/
|
|
|
163
557
|
├── .mj-sync.json # Defines entity: "Templates"
|
|
164
558
|
├── email/
|
|
165
559
|
│ ├── .mj-folder.json # Folder metadata
|
|
166
|
-
│ ├── welcome.json
|
|
560
|
+
│ ├── .welcome.json # Template record (dot-prefixed)
|
|
167
561
|
│ └── welcome.template.html # Template content (referenced)
|
|
168
562
|
└── reports/
|
|
169
563
|
├── .mj-folder.json # Folder metadata
|
|
170
|
-
├── invoice.json
|
|
564
|
+
├── .invoice.json # Template record (dot-prefixed)
|
|
171
565
|
└── invoice.template.html # Template content (referenced)
|
|
172
566
|
```
|
|
173
567
|
|
|
174
568
|
## JSON Metadata Format
|
|
175
569
|
|
|
176
|
-
### Individual Record (e.g., ai-prompts/customer-service
|
|
570
|
+
### Individual Record (e.g., ai-prompts/customer-service/.greeting.json)
|
|
177
571
|
```json
|
|
178
572
|
{
|
|
179
573
|
"fields": {
|
|
@@ -457,14 +851,129 @@ Configuration follows a hierarchical structure:
|
|
|
457
851
|
- **Entity configs**: Each entity directory has its own config defining the entity type
|
|
458
852
|
- **Inheritance**: All files within an entity directory are treated as records of that entity type
|
|
459
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
|
+
|
|
460
960
|
### Root Configuration (metadata/.mj-sync.json)
|
|
461
961
|
```json
|
|
462
962
|
{
|
|
463
963
|
"version": "1.0.0",
|
|
964
|
+
"directoryOrder": [
|
|
965
|
+
"prompts",
|
|
966
|
+
"agent-types"
|
|
967
|
+
],
|
|
464
968
|
"push": {
|
|
465
969
|
"validateBeforePush": true,
|
|
466
970
|
"requireConfirmation": true
|
|
467
971
|
},
|
|
972
|
+
"sqlLogging": {
|
|
973
|
+
"enabled": true,
|
|
974
|
+
"outputDirectory": "./sql_logging",
|
|
975
|
+
"formatAsMigration": false
|
|
976
|
+
},
|
|
468
977
|
"watch": {
|
|
469
978
|
"debounceMs": 1000,
|
|
470
979
|
"ignorePatterns": ["*.tmp", "*.bak"]
|
|
@@ -476,7 +985,7 @@ Configuration follows a hierarchical structure:
|
|
|
476
985
|
```json
|
|
477
986
|
{
|
|
478
987
|
"entity": "AI Prompts",
|
|
479
|
-
"filePattern": "
|
|
988
|
+
"filePattern": ".*.json",
|
|
480
989
|
"defaults": {
|
|
481
990
|
"TypeID": "@lookup:AI Prompt Types.Name=Chat",
|
|
482
991
|
"Temperature": 0.7,
|
|
@@ -484,11 +993,21 @@ Configuration follows a hierarchical structure:
|
|
|
484
993
|
"Status": "Active"
|
|
485
994
|
},
|
|
486
995
|
"pull": {
|
|
996
|
+
"filePattern": ".*.json",
|
|
997
|
+
"updateExistingRecords": true,
|
|
998
|
+
"createNewFileIfNotFound": true,
|
|
999
|
+
"mergeStrategy": "merge",
|
|
487
1000
|
"filter": "Status = 'Active'",
|
|
1001
|
+
"externalizeFields": [
|
|
1002
|
+
{
|
|
1003
|
+
"field": "Prompt",
|
|
1004
|
+
"pattern": "@file:{Name}.prompt.md"
|
|
1005
|
+
}
|
|
1006
|
+
],
|
|
488
1007
|
"relatedEntities": {
|
|
489
1008
|
"MJ: AI Prompt Models": {
|
|
490
1009
|
"entity": "MJ: AI Prompt Models",
|
|
491
|
-
"foreignKey": "
|
|
1010
|
+
"foreignKey": "PromptID",
|
|
492
1011
|
"filter": "Status = 'Active'"
|
|
493
1012
|
}
|
|
494
1013
|
}
|
|
@@ -528,27 +1047,307 @@ The tool now supports managing related entities as embedded collections within p
|
|
|
528
1047
|
- **Cleaner Organization**: Fewer files to manage
|
|
529
1048
|
- **Relationship Clarity**: Visual representation of data relationships
|
|
530
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
|
+
|
|
531
1152
|
### Configuration for Pull
|
|
532
|
-
|
|
1153
|
+
|
|
1154
|
+
The pull command now supports smart update capabilities with extensive configuration options:
|
|
1155
|
+
|
|
533
1156
|
```json
|
|
534
1157
|
{
|
|
535
1158
|
"entity": "AI Prompts",
|
|
1159
|
+
"filePattern": ".*.json",
|
|
536
1160
|
"pull": {
|
|
1161
|
+
"filePattern": ".*.json",
|
|
1162
|
+
"createNewFileIfNotFound": true,
|
|
1163
|
+
"newFileName": ".all-new.json",
|
|
1164
|
+
"appendRecordsToExistingFile": true,
|
|
1165
|
+
"updateExistingRecords": true,
|
|
1166
|
+
"preserveFields": ["customField", "localNotes"],
|
|
1167
|
+
"mergeStrategy": "merge",
|
|
1168
|
+
"backupBeforeUpdate": true,
|
|
1169
|
+
"filter": "Status = 'Active'",
|
|
1170
|
+
"externalizeFields": [
|
|
1171
|
+
{
|
|
1172
|
+
"field": "TemplateText",
|
|
1173
|
+
"pattern": "@file:{Name}.template.md"
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
"field": "PromptText",
|
|
1177
|
+
"pattern": "@file:prompts/{Name}.prompt.md"
|
|
1178
|
+
}
|
|
1179
|
+
],
|
|
1180
|
+
"excludeFields": ["InternalID", "TempField"],
|
|
1181
|
+
"lookupFields": {
|
|
1182
|
+
"CategoryID": {
|
|
1183
|
+
"entity": "AI Prompt Categories",
|
|
1184
|
+
"field": "Name"
|
|
1185
|
+
},
|
|
1186
|
+
"TypeID": {
|
|
1187
|
+
"entity": "AI Prompt Types",
|
|
1188
|
+
"field": "Name"
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
537
1191
|
"relatedEntities": {
|
|
538
1192
|
"MJ: AI Prompt Models": {
|
|
539
1193
|
"entity": "MJ: AI Prompt Models",
|
|
540
|
-
"foreignKey": "
|
|
541
|
-
"filter": "Status = 'Active'"
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
1194
|
+
"foreignKey": "PromptID",
|
|
1195
|
+
"filter": "Status = 'Active'",
|
|
1196
|
+
"lookupFields": {
|
|
1197
|
+
"ModelID": {
|
|
1198
|
+
"entity": "AI Models",
|
|
1199
|
+
"field": "Name"
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
546
1202
|
}
|
|
547
1203
|
}
|
|
548
1204
|
}
|
|
549
1205
|
}
|
|
550
1206
|
```
|
|
551
1207
|
|
|
1208
|
+
#### Pull Configuration Options
|
|
1209
|
+
|
|
1210
|
+
| Option | Type | Default | Description |
|
|
1211
|
+
|--------|------|---------|-------------|
|
|
1212
|
+
| `filePattern` | string | Entity filePattern | Pattern for finding existing files to update |
|
|
1213
|
+
| `createNewFileIfNotFound` | boolean | true | Create files for records not found locally |
|
|
1214
|
+
| `newFileName` | string | - | Filename for new records when appending (see warning below) |
|
|
1215
|
+
| `appendRecordsToExistingFile` | boolean | false | Append new records to a single file |
|
|
1216
|
+
| `updateExistingRecords` | boolean | true | Update existing records found in local files |
|
|
1217
|
+
| `preserveFields` | string[] | [] | Fields that retain local values during updates (see detailed explanation below) |
|
|
1218
|
+
| `mergeStrategy` | string | "merge" | How to merge updates: "merge", "overwrite", or "skip" |
|
|
1219
|
+
| `backupBeforeUpdate` | boolean | false | Create timestamped backups before updating files |
|
|
1220
|
+
| `backupDirectory` | string | ".backups" | Directory name for backup files (relative to entity directory) |
|
|
1221
|
+
| `filter` | string | - | SQL WHERE clause for filtering records |
|
|
1222
|
+
| `externalizeFields` | array/object | - | Fields to save as external files with optional patterns |
|
|
1223
|
+
| `excludeFields` | string[] | [] | Fields to completely omit from pulled data (see detailed explanation below) |
|
|
1224
|
+
| `lookupFields` | object | - | Foreign keys to convert to @lookup references |
|
|
1225
|
+
| `relatedEntities` | object | - | Related entities to pull as embedded collections |
|
|
1226
|
+
|
|
1227
|
+
> **⚠️ Important Configuration Warning**
|
|
1228
|
+
>
|
|
1229
|
+
> When both `appendRecordsToExistingFile: true` and `newFileName` are set, ALL new records will be appended to the single file specified by `newFileName`, effectively ignoring the standard per-record file pattern. This can lead to unexpected file organization:
|
|
1230
|
+
>
|
|
1231
|
+
> ```json
|
|
1232
|
+
> // This configuration will put ALL new records in .all-new.json
|
|
1233
|
+
> "pull": {
|
|
1234
|
+
> "appendRecordsToExistingFile": true,
|
|
1235
|
+
> "newFileName": ".all-new.json" // ⚠️ Overrides individual file creation
|
|
1236
|
+
> }
|
|
1237
|
+
> ```
|
|
1238
|
+
>
|
|
1239
|
+
> **Recommended configurations:**
|
|
1240
|
+
> - For individual files per record: Set `appendRecordsToExistingFile: false` (or omit it)
|
|
1241
|
+
> - For grouped new records: Set both `appendRecordsToExistingFile: true` and `newFileName`
|
|
1242
|
+
> - For mixed approach: Omit `newFileName` to let new records follow the standard pattern
|
|
1243
|
+
|
|
1244
|
+
#### Merge Strategies
|
|
1245
|
+
|
|
1246
|
+
- **`merge`** (default): Combines fields from database and local file, with database values taking precedence for existing fields
|
|
1247
|
+
- **`overwrite`**: Completely replaces local record with database version (except preserved fields)
|
|
1248
|
+
- **`skip`**: Leaves existing records unchanged, only adds new records
|
|
1249
|
+
|
|
1250
|
+
#### Understanding excludeFields vs preserveFields
|
|
1251
|
+
|
|
1252
|
+
These two configuration options serve different purposes for managing fields during pull operations:
|
|
1253
|
+
|
|
1254
|
+
##### excludeFields
|
|
1255
|
+
- **Purpose**: Completely omit specified fields from your local files
|
|
1256
|
+
- **Use Case**: Remove internal/system fields you don't want in version control
|
|
1257
|
+
- **Effect**: Fields never appear in the JSON files
|
|
1258
|
+
- **Example**: Excluding internal IDs, timestamps, or sensitive data
|
|
1259
|
+
|
|
1260
|
+
##### preserveFields
|
|
1261
|
+
- **Purpose**: Protect local customizations from being overwritten during updates
|
|
1262
|
+
- **Use Case**: Keep locally modified values while updating other fields
|
|
1263
|
+
- **Effect**: Fields exist in files but retain their local values during pull
|
|
1264
|
+
- **Example**: Preserving custom file paths, local notes, or environment-specific values
|
|
1265
|
+
- **Special Behavior for @file: references**: When a preserved field contains a `@file:` reference, the tool will update the content at the existing file path rather than creating a new file with a generated name
|
|
1266
|
+
|
|
1267
|
+
##### Example Configuration
|
|
1268
|
+
```json
|
|
1269
|
+
{
|
|
1270
|
+
"pull": {
|
|
1271
|
+
"excludeFields": ["TemplateID", "InternalNotes", "CreatedAt"],
|
|
1272
|
+
"preserveFields": ["TemplateText", "OutputExample", "LocalConfig"]
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
With this configuration:
|
|
1278
|
+
- **TemplateID, InternalNotes, CreatedAt** → Never appear in local files
|
|
1279
|
+
- **TemplateText, OutputExample, LocalConfig** → Keep their local values during updates
|
|
1280
|
+
|
|
1281
|
+
##### Common Scenario: Customized File References
|
|
1282
|
+
When you customize file paths (e.g., changing `@file:templates/skip-conductor.md` to `@file:templates/conductor.md`), use `preserveFields` to protect these customizations:
|
|
1283
|
+
|
|
1284
|
+
```json
|
|
1285
|
+
{
|
|
1286
|
+
"pull": {
|
|
1287
|
+
"preserveFields": ["TemplateText", "OutputExample"],
|
|
1288
|
+
"externalizeFields": [
|
|
1289
|
+
{
|
|
1290
|
+
"field": "TemplateText",
|
|
1291
|
+
"pattern": "@file:templates/{Name}.template.md"
|
|
1292
|
+
}
|
|
1293
|
+
]
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
```
|
|
1297
|
+
|
|
1298
|
+
This ensures your custom paths aren't overwritten when pulling updates from the database.
|
|
1299
|
+
|
|
1300
|
+
**How it works:**
|
|
1301
|
+
1. **Without preserveFields**: Pull would create a new file using the pattern (e.g., `templates/skip-conductor.template.md`) and update the JSON to point to it
|
|
1302
|
+
2. **With preserveFields**: Pull keeps your custom path (e.g., `@file:templates/conductor.md`) in the JSON and updates the content at that existing location
|
|
1303
|
+
|
|
1304
|
+
This is particularly useful when:
|
|
1305
|
+
- You've reorganized your file structure after initial pull
|
|
1306
|
+
- You've renamed files to follow your own naming conventions
|
|
1307
|
+
- You want to maintain consistent paths across team members
|
|
1308
|
+
|
|
1309
|
+
#### Backup Configuration
|
|
1310
|
+
|
|
1311
|
+
When `backupBeforeUpdate` is enabled, the tool creates timestamped backups before updating existing files:
|
|
1312
|
+
|
|
1313
|
+
- **Backup Location**: Files are backed up to the `backupDirectory` (default: `.backups`) within the entity directory
|
|
1314
|
+
- **Backup Naming**: Original filename + timestamp + `.backup` extension (e.g., `.greeting.json` → `.greeting.2024-03-15T10-30-45-123Z.backup`)
|
|
1315
|
+
- **Extension**: All backup files use the `.backup` extension, preventing them from being processed by push/pull/status commands
|
|
1316
|
+
- **Deduplication**: Only one backup is created per file per pull operation, even if the file contains multiple records
|
|
1317
|
+
|
|
1318
|
+
Example configuration:
|
|
1319
|
+
```json
|
|
1320
|
+
"pull": {
|
|
1321
|
+
"backupBeforeUpdate": true,
|
|
1322
|
+
"backupDirectory": ".backups" // Custom backup directory name
|
|
1323
|
+
}
|
|
1324
|
+
```
|
|
1325
|
+
|
|
1326
|
+
#### Externalize Fields Patterns
|
|
1327
|
+
|
|
1328
|
+
The `externalizeFields` configuration supports dynamic file naming with placeholders:
|
|
1329
|
+
|
|
1330
|
+
```json
|
|
1331
|
+
"externalizeFields": [
|
|
1332
|
+
{
|
|
1333
|
+
"field": "TemplateText",
|
|
1334
|
+
"pattern": "@file:{Name}.template.md"
|
|
1335
|
+
},
|
|
1336
|
+
{
|
|
1337
|
+
"field": "SQLQuery",
|
|
1338
|
+
"pattern": "@file:queries/{CategoryName}/{Name}.sql"
|
|
1339
|
+
}
|
|
1340
|
+
]
|
|
1341
|
+
```
|
|
1342
|
+
|
|
1343
|
+
Supported placeholders:
|
|
1344
|
+
- `{Name}` - The entity's name field value
|
|
1345
|
+
- `{ID}` - The entity's primary key
|
|
1346
|
+
- `{FieldName}` - The field being externalized
|
|
1347
|
+
- `{AnyFieldName}` - Any field from the entity record
|
|
1348
|
+
|
|
1349
|
+
All values are sanitized for filesystem compatibility (lowercase, spaces to hyphens, special characters removed).
|
|
1350
|
+
|
|
552
1351
|
### Nested Related Entities
|
|
553
1352
|
Support for multiple levels of nesting:
|
|
554
1353
|
```json
|