@memberjunction/metadata-sync 5.37.0 → 5.38.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.
@@ -0,0 +1,454 @@
1
+ /**
2
+ * @fileoverview Upfront preloading + lookup/file caching for `mj sync push`.
3
+ *
4
+ * `SyncMetadataEngine` extends `BaseEngine` to piggy-back on its batched
5
+ * `RunViews` loader and `BaseEntity` event-bus subscription. The push flow
6
+ * uses it in three ways:
7
+ *
8
+ * 1. **Preload** — `Config()` scans every JSON file under each entity
9
+ * directory once, collects the set of entity names touched (including
10
+ * nested `relatedEntities`), and asks `BaseEngine.Load` to issue one
11
+ * unfiltered `RunView` per entity. We load *all* rows of each touched
12
+ * entity, not just the PKs mentioned in files — for two reasons:
13
+ *
14
+ * (a) `mj sync` is for METADATA. The entities involved (Actions,
15
+ * Prompts, Agents, Templates, etc.) are inherently bounded —
16
+ * hundreds to low tens of thousands of rows per tenant, not
17
+ * millions. Loading everything is faster than computing and
18
+ * shipping a giant `WHERE IN (...)` clause.
19
+ * (b) `@lookup:` resolution often targets records that aren't in
20
+ * the local files (e.g. a category referenced by name). A
21
+ * full preload means those lookups hit the cache too.
22
+ *
23
+ * The loaded `BaseEntity` instances live on dynamic properties named
24
+ * via {@link getPropertyNameForEntity}; lookups during the sync phase
25
+ * consult those arrays instead of round-tripping the DB.
26
+ *
27
+ * 2. **Event-driven mutation** — `BaseEngine` already wires up the global
28
+ * `BaseEntity` event bus. Because our configs carry no Filter/OrderBy,
29
+ * the base class's default `canUseImmediateMutation` path handles
30
+ * save/delete/remote-invalidate events automatically.
31
+ *
32
+ * 3. **Lookup + file caches** — Resolved `@lookup` keys are memoized in
33
+ * {@link lookupCache} and parsed file contents are memoized in
34
+ * {@link fileDataCache}, so validation and sync passes don't re-read
35
+ * or re-parse the same file twice.
36
+ *
37
+ * @module sync-metadata-engine
38
+ */
39
+ import { BaseEngine } from '@memberjunction/core';
40
+ import { IsUuidSQLType } from '@memberjunction/sql-dialect';
41
+ import fastGlob from 'fast-glob';
42
+ import fs from 'fs-extra';
43
+ import { JsonPreprocessor } from './json-preprocessor.js';
44
+ import { loadEntityConfig } from '../config.js';
45
+ /** Prefix used to namespace the dynamic per-entity cache properties on the engine instance. */
46
+ const CACHED_ENTITY_PROP_PREFIX = 'cached_';
47
+ /** Concurrency cap for parallel file reads during preload. */
48
+ const FILE_READ_CONCURRENCY = 16;
49
+ /**
50
+ * Emit a warning when any single preloaded entity comes back with more rows
51
+ * than this. `mj sync` is for metadata — entities in this benchmark band
52
+ * (Actions, Prompts, Templates, etc.) are bounded by design. Loading 100k+
53
+ * rows of one entity into RAM is a signal that something is being synced
54
+ * that shouldn't be (or the metadata set has grown beyond the tool's intent).
55
+ */
56
+ const LARGE_ENTITY_WARN_THRESHOLD = 100_000;
57
+ /**
58
+ * Coordinates upfront batch-preloading of `BaseEntity` instances and in-memory
59
+ * caches for `mj sync push`. See module docstring for the full picture.
60
+ */
61
+ export class SyncMetadataEngine extends BaseEngine {
62
+ constructor() {
63
+ super(...arguments);
64
+ this.entityDirs = [];
65
+ this.preloadedEntityNames = new Set();
66
+ this.lookupCache = new Map();
67
+ this.fileDataCache = new Map();
68
+ /** Reverse index used to scope lookup invalidation on delete: entity name → set of lookup keys touching that entity. */
69
+ this.lookupKeysByEntity = new Map();
70
+ /**
71
+ * O(1) PK lookup index built after preload completes. Keyed by entity
72
+ * name, then by `serializePrimaryKey(...)` of the entity's own PK.
73
+ *
74
+ * Without this, `loadEntity` falls into an `Array.find(... GetAll())`
75
+ * scan over the entire preloaded slot, which is O(N) per record being
76
+ * processed — i.e. O(N×K) overall. For entities like `MJ: Integration
77
+ * Object Fields` where DB-wide row count can be 10×+ the file count,
78
+ * that turns into billions of comparisons (38 min for the integrations
79
+ * dir alone in real-world measurement).
80
+ *
81
+ * Kept in sync by {@link addEntityToCache} / {@link removeEntityFromCache}.
82
+ * `findCachedByPrimaryKey` falls back to an array scan on Map miss so
83
+ * we tolerate drift from `BaseEngine`'s event-driven slot mutations
84
+ * (which mutate the array directly without going through our helpers).
85
+ */
86
+ this.pkIndexes = new Map();
87
+ /** Non-fatal warnings collected during preload (malformed shapes, oversized entities). Drained by the caller after `Config()`. */
88
+ this.warnings = [];
89
+ }
90
+ initializeEngine(syncEngine) {
91
+ this.syncEngine = syncEngine;
92
+ }
93
+ setEntityDirs(dirs) {
94
+ this.entityDirs = dirs;
95
+ }
96
+ /** Returns and clears the warnings collected during the last preload run. */
97
+ drainWarnings() {
98
+ const out = this.warnings;
99
+ this.warnings = [];
100
+ return out;
101
+ }
102
+ /**
103
+ * Property name on this engine instance under which cached `BaseEntity[]`
104
+ * arrays for the given entity are stored. `BaseEngine.applyImmediateMutation`
105
+ * reads/writes these slots directly, so we use a deterministic naming scheme.
106
+ */
107
+ getPropertyNameForEntity(entityName) {
108
+ return `${CACHED_ENTITY_PROP_PREFIX}${entityName.replace(/[^a-zA-Z0-9]/g, '_')}`;
109
+ }
110
+ /**
111
+ * Build the dynamic configuration list and trigger `BaseEngine.Load` —
112
+ * which fans the per-entity unfiltered views out over a single `RunViews`
113
+ * batch under the hood. Then check each loaded slot for size and emit
114
+ * warnings if anything came back larger than the metadata-scale threshold.
115
+ */
116
+ async Config(forceRefresh, contextUser, provider) {
117
+ const configs = await this.buildDynamicConfigs();
118
+ const providerToUse = provider || this.ProviderToUse;
119
+ if (configs.length > 0) {
120
+ await this.Load(configs, providerToUse, forceRefresh, contextUser);
121
+ this.buildPKIndexes(configs);
122
+ this.warnOnOversizedSlots(configs);
123
+ }
124
+ return true;
125
+ }
126
+ /**
127
+ * Build the per-entity PK → entity Map from each populated slot.
128
+ * Runs once after `BaseEngine.Load` completes. The cost (one walk
129
+ * over the loaded arrays, building Map entries) is trivial compared
130
+ * to the per-record-lookup savings it enables downstream.
131
+ */
132
+ buildPKIndexes(configs) {
133
+ for (const config of configs) {
134
+ if (!config.PropertyName || !config.EntityName)
135
+ continue;
136
+ const entityInfo = this.syncEngine.getEntityInfo(config.EntityName);
137
+ if (!entityInfo)
138
+ continue;
139
+ const list = this[config.PropertyName];
140
+ if (!Array.isArray(list))
141
+ continue;
142
+ const index = new Map();
143
+ for (const entity of list) {
144
+ try {
145
+ const pkStr = this.serializePrimaryKey(entityInfo, entity.GetAll());
146
+ index.set(pkStr, entity);
147
+ }
148
+ catch {
149
+ // Defensive: a malformed cached entity shouldn't break index
150
+ // construction for the whole entity. The array scan fallback
151
+ // will still find it if needed.
152
+ }
153
+ }
154
+ this.pkIndexes.set(config.EntityName, index);
155
+ }
156
+ }
157
+ /**
158
+ * O(1) PK lookup against the preload cache, with an array-scan
159
+ * fallback so we tolerate drift from `BaseEngine` event-driven
160
+ * slot mutations that don't go through our `addEntityToCache` helper.
161
+ *
162
+ * Returns `null` when the entity isn't preloaded or the PK isn't in
163
+ * the cache — callers fall through to a DB load.
164
+ */
165
+ findCachedByPrimaryKey(entityName, primaryKey) {
166
+ if (!this.isEntityPreloaded(entityName))
167
+ return null;
168
+ const entityInfo = this.syncEngine.getEntityInfo(entityName);
169
+ if (!entityInfo)
170
+ return null;
171
+ const pkStr = this.serializePrimaryKey(entityInfo, primaryKey);
172
+ const index = this.pkIndexes.get(entityName);
173
+ const hit = index?.get(pkStr);
174
+ if (hit)
175
+ return hit;
176
+ // Fallback: someone (BaseEngine event handler) might have spliced
177
+ // a new entity into the array slot without updating the index.
178
+ // Linear scan once, and if we find a match, repair the index so
179
+ // subsequent hits stay O(1).
180
+ const list = this.getCachedEntities(entityName);
181
+ for (const entity of list) {
182
+ try {
183
+ if (this.serializePrimaryKey(entityInfo, entity.GetAll()) === pkStr) {
184
+ index?.set(pkStr, entity);
185
+ return entity;
186
+ }
187
+ }
188
+ catch {
189
+ // skip malformed entries
190
+ }
191
+ }
192
+ return null;
193
+ }
194
+ markEntityAsPreloaded(entityName) {
195
+ this.preloadedEntityNames.add(entityName);
196
+ }
197
+ isEntityPreloaded(entityName) {
198
+ return this.preloadedEntityNames.has(entityName);
199
+ }
200
+ getCachedEntities(entityName) {
201
+ const propName = this.getPropertyNameForEntity(entityName);
202
+ const list = this[propName];
203
+ return Array.isArray(list) ? list : [];
204
+ }
205
+ getCachedFile(filePath) {
206
+ return this.fileDataCache.get(filePath);
207
+ }
208
+ cacheFile(filePath, rawData, fileData) {
209
+ this.fileDataCache.set(filePath, { rawData, fileData });
210
+ }
211
+ /**
212
+ * Invalidate the cached file content for a path. Call this immediately
213
+ * before (or after) any code path that writes back to a metadata JSON
214
+ * file during the sync, so a subsequent reader gets fresh contents.
215
+ */
216
+ invalidateCachedFile(filePath) {
217
+ this.fileDataCache.delete(filePath);
218
+ }
219
+ addEntityToCache(entityName, entity) {
220
+ const propName = this.getPropertyNameForEntity(entityName);
221
+ const slot = this;
222
+ if (!Array.isArray(slot[propName])) {
223
+ slot[propName] = [];
224
+ }
225
+ const list = slot[propName];
226
+ // `PrimaryKey.Equals` requires the entity to have a populated composite
227
+ // key; if either side is missing one we fall back to reference equality
228
+ // only (the entity might not be saved yet, in which case no dedup is
229
+ // possible anyway).
230
+ const incomingKey = entity.PrimaryKey;
231
+ const arrayIdx = list.findIndex(e => {
232
+ if (e === entity)
233
+ return true;
234
+ const existingKey = e.PrimaryKey;
235
+ if (!incomingKey || !existingKey)
236
+ return false;
237
+ return existingKey.Equals(incomingKey);
238
+ });
239
+ if (arrayIdx >= 0) {
240
+ list[arrayIdx] = entity;
241
+ }
242
+ else {
243
+ list.push(entity);
244
+ }
245
+ // Keep the PK index in sync. Skip silently when serializePrimaryKey
246
+ // throws — typically a half-built entity with no PK populated yet.
247
+ try {
248
+ const entityInfo = this.syncEngine.getEntityInfo(entityName);
249
+ if (entityInfo) {
250
+ const pkStr = this.serializePrimaryKey(entityInfo, entity.GetAll());
251
+ let index = this.pkIndexes.get(entityName);
252
+ if (!index) {
253
+ index = new Map();
254
+ this.pkIndexes.set(entityName, index);
255
+ }
256
+ index.set(pkStr, entity);
257
+ }
258
+ }
259
+ catch { /* defensive: malformed entity */ }
260
+ }
261
+ removeEntityFromCache(entityName, primaryKey) {
262
+ const propName = this.getPropertyNameForEntity(entityName);
263
+ const list = this[propName];
264
+ const entityInfo = this.syncEngine.getEntityInfo(entityName);
265
+ let pkStr = null;
266
+ if (entityInfo) {
267
+ pkStr = this.serializePrimaryKey(entityInfo, primaryKey);
268
+ if (list) {
269
+ const arrayIdx = list.findIndex(e => this.serializePrimaryKey(entityInfo, e.GetAll()) === pkStr);
270
+ if (arrayIdx >= 0) {
271
+ list.splice(arrayIdx, 1);
272
+ }
273
+ }
274
+ }
275
+ if (pkStr !== null) {
276
+ this.pkIndexes.get(entityName)?.delete(pkStr);
277
+ }
278
+ this.invalidateLookupsForEntity(entityName);
279
+ }
280
+ getCachedLookup(key) {
281
+ return this.lookupCache.get(key);
282
+ }
283
+ setCachedLookup(key, id, entityName) {
284
+ this.lookupCache.set(key, id);
285
+ if (entityName) {
286
+ const normalized = entityName.toLowerCase();
287
+ let bucket = this.lookupKeysByEntity.get(normalized);
288
+ if (!bucket) {
289
+ bucket = new Set();
290
+ this.lookupKeysByEntity.set(normalized, bucket);
291
+ }
292
+ bucket.add(key);
293
+ }
294
+ }
295
+ clearLookupCache() {
296
+ this.lookupCache.clear();
297
+ this.lookupKeysByEntity.clear();
298
+ }
299
+ /**
300
+ * Invalidate only the lookup entries that touch a given entity. Avoids
301
+ * the perf cliff of clearing the whole lookup cache on every delete.
302
+ *
303
+ * Note: if `setCachedLookup` was called without an `entityName`, those
304
+ * entries aren't in the reverse index and won't be cleared here. The
305
+ * `resolveLookup` integration always passes the entity name, so this
306
+ * is only a concern for callers that bypass that path.
307
+ */
308
+ invalidateLookupsForEntity(entityName) {
309
+ const normalized = entityName.toLowerCase();
310
+ const bucket = this.lookupKeysByEntity.get(normalized);
311
+ if (!bucket)
312
+ return;
313
+ for (const key of bucket) {
314
+ this.lookupCache.delete(key);
315
+ }
316
+ this.lookupKeysByEntity.delete(normalized);
317
+ }
318
+ /**
319
+ * Canonical string representation of a record's primary key, used as the
320
+ * key in entity-cache dedup/removal lookups. UUID-typed components are
321
+ * lower-cased to bridge SQL Server (upper) vs PG (lower).
322
+ */
323
+ serializePrimaryKey(entityInfo, primaryKey) {
324
+ const parts = entityInfo.PrimaryKeys.map((pk) => {
325
+ const val = primaryKey[pk.Name];
326
+ let valStr = val !== undefined && val !== null ? String(val) : '';
327
+ const pkType = pk.Type ?? '';
328
+ const normalizedType = pkType.trim().toLowerCase();
329
+ if (IsUuidSQLType(pkType) || normalizedType === 'uniqueidentifier' || normalizedType === 'uuid') {
330
+ valStr = valStr.toLowerCase().trim();
331
+ }
332
+ return `${pk.Name.toLowerCase()}=${valStr}`;
333
+ });
334
+ parts.sort();
335
+ return parts.join('|');
336
+ }
337
+ /**
338
+ * Walk every entity referenced by metadata files (including nested
339
+ * `relatedEntities`) and build one unfiltered `BaseEngine` config per
340
+ * entity. Each config triggers a single `SELECT * FROM vw<Entity>`
341
+ * (capped by `IgnoreMaxRows: true` in BaseEngine), pre-populating the
342
+ * per-entity slot the sync path will read from.
343
+ */
344
+ async buildDynamicConfigs() {
345
+ const entitiesFound = new Set();
346
+ for (const entityDir of this.entityDirs) {
347
+ const entityConfig = await loadEntityConfig(entityDir);
348
+ if (!entityConfig)
349
+ continue;
350
+ const pattern = entityConfig.filePattern || '*.json';
351
+ const files = await fastGlob(pattern, {
352
+ cwd: entityDir,
353
+ absolute: true,
354
+ onlyFiles: true,
355
+ dot: true,
356
+ ignore: ['**/node_modules/**', '**/.mj-*.json']
357
+ });
358
+ await this.processFilesInParallel(files, entityConfig.entity, entitiesFound);
359
+ }
360
+ const configs = [];
361
+ for (const entityName of entitiesFound) {
362
+ const entityInfo = this.syncEngine.getEntityInfo(entityName);
363
+ if (!entityInfo)
364
+ continue;
365
+ this.markEntityAsPreloaded(entityName);
366
+ configs.push({
367
+ PropertyName: this.getPropertyNameForEntity(entityName),
368
+ EntityName: entityName,
369
+ Type: 'entity',
370
+ ResultType: 'entity_object',
371
+ // No Filter — load all rows. Metadata entities are bounded by design;
372
+ // the few thousand-row tables we touch are faster to bulk-load than
373
+ // to fence with a giant WHERE clause. See the module docstring for
374
+ // the full reasoning.
375
+ AutoRefresh: true
376
+ });
377
+ }
378
+ return configs;
379
+ }
380
+ /**
381
+ * After `BaseEngine.Load` finishes, walk the configs and check whether any
382
+ * single preloaded entity exceeded the metadata-scale threshold. Emit a
383
+ * warning so the operator knows their sync is loading something it
384
+ * probably shouldn't be.
385
+ */
386
+ warnOnOversizedSlots(configs) {
387
+ for (const config of configs) {
388
+ if (!config.PropertyName || !config.EntityName)
389
+ continue;
390
+ const list = this[config.PropertyName];
391
+ if (!Array.isArray(list))
392
+ continue;
393
+ if (list.length > LARGE_ENTITY_WARN_THRESHOLD) {
394
+ this.warnings.push(`Preloaded ${list.length.toLocaleString()} rows of '${config.EntityName}'. mj sync is intended for metadata-scale entities (hundreds to low tens of thousands); this is large enough that you may want to confirm this entity belongs in a sync workflow.`);
395
+ }
396
+ }
397
+ }
398
+ /**
399
+ * Recursively walk records collecting every entity name they (or their
400
+ * `relatedEntities`) reference. We don't collect primary keys because
401
+ * preload is unfiltered — knowing the entity is enough.
402
+ *
403
+ * Malformed `relatedEntities` shapes (non-array values) produce a warning
404
+ * pointing at the offending file rather than silently being skipped.
405
+ */
406
+ collectEntitiesRecursive(records, entityName, entitiesFound, filePath) {
407
+ entitiesFound.add(entityName);
408
+ for (const record of records) {
409
+ const deleteRecord = record.deleteRecord;
410
+ if (deleteRecord?.delete === true) {
411
+ continue;
412
+ }
413
+ const relatedEntities = record.relatedEntities;
414
+ if (!relatedEntities)
415
+ continue;
416
+ for (const [relatedEntityName, relatedRecords] of Object.entries(relatedEntities)) {
417
+ if (Array.isArray(relatedRecords)) {
418
+ this.collectEntitiesRecursive(relatedRecords, relatedEntityName, entitiesFound, filePath);
419
+ }
420
+ else {
421
+ this.warnings.push(`Skipped preload for relatedEntities['${relatedEntityName}'] in ${filePath}: expected an array, got ${typeof relatedRecords}.`);
422
+ }
423
+ }
424
+ }
425
+ }
426
+ /**
427
+ * Process file reads in bounded-parallel batches. `fs.readJson` is I/O-bound;
428
+ * doing this sequentially was the dominant cost on metadata trees with
429
+ * thousands of small files.
430
+ */
431
+ async processFilesInParallel(files, entityName, entitiesFound) {
432
+ for (let i = 0; i < files.length; i += FILE_READ_CONCURRENCY) {
433
+ const batch = files.slice(i, i + FILE_READ_CONCURRENCY);
434
+ const parsed = await Promise.all(batch.map(filePath => this.readAndPreprocessFile(filePath)));
435
+ for (const { filePath, rawData, fileData } of parsed) {
436
+ this.cacheFile(filePath, rawData, fileData);
437
+ const records = Array.isArray(fileData) ? fileData : [fileData];
438
+ this.collectEntitiesRecursive(records, entityName, entitiesFound, filePath);
439
+ }
440
+ }
441
+ }
442
+ async readAndPreprocessFile(filePath) {
443
+ const rawData = await fs.readJson(filePath);
444
+ let fileData = rawData;
445
+ const jsonString = JSON.stringify(rawData);
446
+ const hasIncludes = jsonString.includes('"@include"') || jsonString.includes('"@include.');
447
+ if (hasIncludes) {
448
+ const preprocessor = new JsonPreprocessor();
449
+ fileData = await preprocessor.processFile(filePath);
450
+ }
451
+ return { filePath, rawData, fileData };
452
+ }
453
+ }
454
+ //# sourceMappingURL=sync-metadata-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-metadata-engine.js","sourceRoot":"","sources":["../../src/lib/sync-metadata-engine.ts"],"names":[],"mappings":"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"}
@@ -48,6 +48,7 @@ export declare class PushService {
48
48
  private deferredFileWrites;
49
49
  private deferredRecords;
50
50
  private stateManager;
51
+ private syncMetadataEngine;
51
52
  constructor(syncEngine: SyncEngine, contextUser: UserInfo, stateManager?: SyncStateManager);
52
53
  /** Set or replace the state manager after construction. */
53
54
  setStateManager(stateManager: SyncStateManager): void;
@@ -1 +1 @@
1
- {"version":3,"file":"PushService.d.ts","sourceRoot":"","sources":["../../src/services/PushService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PushService.d.ts","sourceRoot":"","sources":["../../src/services/PushService.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import { Metadata } from '@memberjunction/core';
5
5
  import { UUIDsEqual } from '@memberjunction/global';
6
6
  import { IsStringSQLType } from '@memberjunction/sql-dialect';
7
7
  import { DeferrableLookupError } from '../lib/sync-engine.js';
8
+ import { SyncMetadataEngine } from '../lib/sync-metadata-engine.js';
8
9
  import { BatchContextIndex } from '../lib/batch-context-index.js';
9
10
  import { loadEntityConfig, loadSyncConfig } from '../config.js';
10
11
  import { FileBackupManager } from '../lib/file-backup-manager.js';
@@ -34,6 +35,10 @@ export class PushService {
34
35
  this.syncEngine = syncEngine;
35
36
  this.contextUser = contextUser;
36
37
  this.stateManager = stateManager;
38
+ // Initialize SyncMetadataEngine
39
+ this.syncMetadataEngine = new SyncMetadataEngine();
40
+ this.syncMetadataEngine.initializeEngine(this.syncEngine);
41
+ this.syncEngine.setMetadataEngine(this.syncMetadataEngine);
37
42
  }
38
43
  /** Set or replace the state manager after construction. */
39
44
  setStateManager(stateManager) {
@@ -152,6 +157,18 @@ export class PushService {
152
157
  if (entityDirs.length === 0) {
153
158
  throw new Error('No entity directories found');
154
159
  }
160
+ // Preload entities and cache files.
161
+ // We pass the SyncEngine's provider explicitly rather than reaching for
162
+ // Metadata.Provider — `mj sync` is single-process so they resolve to the
163
+ // same instance today, but routing through SyncEngine keeps the wiring
164
+ // self-consistent and makes future provider plumbing trivial.
165
+ callbacks?.onLog?.('⚡ Preloading metadata and caching files...');
166
+ this.syncMetadataEngine.setEntityDirs(entityDirs);
167
+ await this.syncMetadataEngine.Config(true, this.contextUser, this.syncEngine.getProvider());
168
+ for (const warning of this.syncMetadataEngine.drainWarnings()) {
169
+ callbacks?.onWarn?.(` ⚠️ ${warning}`);
170
+ }
171
+ callbacks?.onLog?.('✓ Preload completed successfully\n');
155
172
  if (options.verbose) {
156
173
  callbacks?.onLog?.(`Found ${entityDirs.length} entity ${entityDirs.length === 1 ? 'directory' : 'directories'} to process`);
157
174
  }
@@ -390,20 +407,29 @@ export class PushService {
390
407
  if (!options.dryRun) {
391
408
  await fileBackupManager.backupFile(filePath);
392
409
  }
393
- // Read the raw file data first
394
- const rawFileData = await fs.readJson(filePath);
410
+ // Check if file is already cached to avoid double I/O
411
+ let rawFileData;
412
+ let fileData;
413
+ const cachedFile = this.syncMetadataEngine.getCachedFile(filePath);
414
+ if (cachedFile) {
415
+ rawFileData = cachedFile.rawData;
416
+ fileData = cachedFile.fileData;
417
+ }
418
+ else {
419
+ // Fallback (e.g. if not preloaded)
420
+ rawFileData = await fs.readJson(filePath);
421
+ fileData = rawFileData;
422
+ const jsonString = JSON.stringify(rawFileData);
423
+ const hasIncludes = jsonString.includes('"@include"') || jsonString.includes('"@include.');
424
+ if (hasIncludes) {
425
+ const jsonPreprocessor = new JsonPreprocessor();
426
+ fileData = await jsonPreprocessor.processFile(filePath);
427
+ }
428
+ this.syncMetadataEngine.cacheFile(filePath, rawFileData, fileData);
429
+ }
395
430
  // Keep unprocessed data to write back (preserves @file: references)
396
431
  const unprocessedRecords = Array.isArray(rawFileData) ? rawFileData : [rawFileData];
397
432
  const isArray = Array.isArray(rawFileData);
398
- // Preprocess @include directives before checksumming, so changes to
399
- // included files are detected by the incremental skip check.
400
- let fileData = rawFileData;
401
- const jsonString = JSON.stringify(rawFileData);
402
- const hasIncludes = jsonString.includes('"@include"') || jsonString.includes('"@include.');
403
- if (hasIncludes) {
404
- const jsonPreprocessor = new JsonPreprocessor();
405
- fileData = await jsonPreprocessor.processFile(filePath);
406
- }
407
433
  // Compute checksum on the RESOLVED content (after @include preprocessing)
408
434
  // so that changes to included files are properly detected.
409
435
  let cachedChecksum;
@@ -584,6 +610,9 @@ export class PushService {
584
610
  else {
585
611
  await JsonWriteHelper.writeOrderedRecordData(filePath, unprocessedRecords[0]);
586
612
  }
613
+ // Drop the cached snapshot — file on disk no longer matches it,
614
+ // and any later reader within this push must see fresh contents.
615
+ this.syncMetadataEngine.invalidateCachedFile(filePath);
587
616
  }
588
617
  }
589
618
  // Update stored checksum after successful processing (reuse cached value if available)
@@ -1243,6 +1272,8 @@ export class PushService {
1243
1272
  }
1244
1273
  throw new Error(`Failed to delete ${entityName} record: ${errorMessage}`);
1245
1274
  }
1275
+ // Remove from metadata cache
1276
+ this.syncMetadataEngine.removeEntityFromCache(entityName, record.primaryKey);
1246
1277
  // Set deletedAt timestamp after successful deletion (only for metadata records)
1247
1278
  if (!isDbOnly) {
1248
1279
  if (!record.deleteRecord) {
@@ -1654,6 +1685,8 @@ export class PushService {
1654
1685
  else {
1655
1686
  await JsonWriteHelper.writeOrderedRecordData(deferredWrite.filePath, deferredWrite.records[0]);
1656
1687
  }
1688
+ // File contents just changed on disk — drop the stale cache entry.
1689
+ this.syncMetadataEngine.invalidateCachedFile(deferredWrite.filePath);
1657
1690
  }
1658
1691
  catch (error) {
1659
1692
  callbacks?.onWarn?.(` ⚠️ Failed to write ${deferredWrite.filePath}: ${error}`);