meadow-integration 1.0.13 → 1.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meadow-integration",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Meadow Data Integration",
5
5
  "bin": {
6
6
  "mdwint": "source/cli/Meadow-Integration-CLI-Run.js"
@@ -46,6 +46,15 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
46
46
  // pull all records in the range from the server instead of subdividing further.
47
47
  this.BisectMinRangeSize = this.options.BisectMinRangeSize || 1000;
48
48
 
49
+ // Tolerance window in milliseconds for cross-database timestamp precision differences.
50
+ // MySQL DATETIME stores whole seconds, MSSQL DATETIME rounds to ~3.33ms increments,
51
+ // PostgreSQL TIMESTAMP stores microseconds, and SQLite stores as TEXT. When comparing
52
+ // timestamps across systems, the maximum rounding error is 1000ms (MySQL second-level
53
+ // truncation). Default 1000ms covers all supported provider combinations.
54
+ this.DateTimePrecisionMS = (typeof(this.options.DateTimePrecisionMS) === 'number')
55
+ ? this.options.DateTimePrecisionMS
56
+ : 1000;
57
+
49
58
  this.Meadow = false;
50
59
 
51
60
  this.operation = new libMeadowOperation(this.fable);
@@ -150,10 +159,31 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
150
159
 
151
160
  // ---- REST / Local query helpers ----
152
161
 
162
+ // Normalize a date value to a UTC dayJS instance.
163
+ //
164
+ // Local dates stored in SQLite are in UTC but formatted without a timezone
165
+ // indicator (e.g. "2022-05-10 22:50:26.000"). When the driver or ORM wraps
166
+ // them in a JavaScript Date object, JS interprets them as local time, shifting
167
+ // the value by the machine's UTC offset. To recover the original naive UTC
168
+ // time, we format through local time (which undoes the offset) then re-parse
169
+ // as UTC. Server dates already carry a "Z" suffix and are parsed correctly.
170
+ _normalizeDateUTC(pDate)
171
+ {
172
+ if (typeof(pDate) === 'string')
173
+ {
174
+ // String dates — strip any trailing Z or timezone so dayJS.utc() treats as UTC
175
+ return this.fable.Dates.dayJS.utc(pDate);
176
+ }
177
+ // Date objects (from SQLite via ORM) — format as local time string to recover
178
+ // the naive stored value, then re-parse as UTC
179
+ let tmpNaiveStr = this.fable.Dates.dayJS(pDate).format('YYYY-MM-DD HH:mm:ss.SSS');
180
+ return this.fable.Dates.dayJS.utc(tmpNaiveStr);
181
+ }
182
+
153
183
  // Format a date value for use in Meadow REST filter expressions (FBV).
154
184
  _formatDateForFilter(pDate)
155
185
  {
156
- return this.fable.Dates.dayJS.utc(pDate).format('YYYY-MM-DDTHH:mm:ss.SSS');
186
+ return this._normalizeDateUTC(pDate).format('YYYY-MM-DDTHH:mm:ss.SSS');
157
187
  }
158
188
 
159
189
  // Get a count from the remote server, optionally filtered.
@@ -433,6 +463,16 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
433
463
  fFetchPage();
434
464
  }
435
465
 
466
+ // Increment the FullSync progress tracker by pCount records checked/synced.
467
+ _incrementProgress(pCount)
468
+ {
469
+ let tmpTracker = this.operation.progressTrackers[`FullSync-${this.EntitySchema.TableName}`];
470
+ if (tmpTracker && pCount > 0)
471
+ {
472
+ tmpTracker.CurrentCount = Math.min(tmpTracker.CurrentCount + pCount, tmpTracker.TotalCount);
473
+ }
474
+ }
475
+
436
476
  // ---- Bisection logic ----
437
477
 
438
478
  // Compare a local ID range against the server. If counts or date boundaries
@@ -470,6 +510,7 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
470
510
  if (!this._hasUpdateDate)
471
511
  {
472
512
  // No UpdateDate column -- counts match, assume in sync
513
+ this._incrementProgress(pServerCount);
473
514
  return fCallback();
474
515
  }
475
516
 
@@ -493,11 +534,12 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
493
534
  }
494
535
 
495
536
  const tmpServerMaxDate = pServerMaxRecords[0].UpdateDate;
496
- const tmpMaxDateDiff = Math.abs(this.fable.Dates.dayJS.utc(pLocalMaxDate).diff(this.fable.Dates.dayJS.utc(tmpServerMaxDate)));
537
+ const tmpMaxDateDiff = Math.abs(this._normalizeDateUTC(pLocalMaxDate).diff(this._normalizeDateUTC(tmpServerMaxDate)));
497
538
 
498
- if (tmpMaxDateDiff < 5)
539
+ if (tmpMaxDateDiff <= this.DateTimePrecisionMS)
499
540
  {
500
541
  // Max dates match and counts match -- this range is in sync
542
+ this._incrementProgress(pServerCount);
501
543
  return fCallback();
502
544
  }
503
545
 
@@ -562,6 +604,7 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
562
604
  {
563
605
  this.fable.log.info(`${this.EntitySchema.TableName}: synced ${pSyncedCount} records in range ${pMinID}-${pMaxID}`);
564
606
  }
607
+ this._incrementProgress(pSyncedCount || 0);
565
608
  return fCallback();
566
609
  });
567
610
  }
@@ -890,6 +933,13 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
890
933
  });
891
934
  },
892
935
 
936
+ // Create a progress tracker so callers (e.g. data-cloner UI) can see Total/Synced
937
+ (fStageComplete) =>
938
+ {
939
+ this.operation.createProgressTracker(tmpSyncState.Server.RecordCount, `FullSync-${this.EntitySchema.TableName}`);
940
+ return fStageComplete();
941
+ },
942
+
893
943
  // ---- Stage 3: UpdateDate-based fast sync ----
894
944
  // If we have UpdateDate, compare server record count up to our local
895
945
  // max UpdateDate. If it matches local count, existing records are in
@@ -926,6 +976,7 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
926
976
  // Record counts match up to our max UpdateDate -- existing records are in sync.
927
977
  this.fable.log.info(`${this.EntitySchema.TableName}: counts match up to local max UpdateDate; existing records appear in sync.`);
928
978
  tmpSyncState.ExistingRecordsInSync = true;
979
+ this._incrementProgress(pServerCountBefore);
929
980
  }
930
981
  else
931
982
  {
@@ -951,6 +1002,7 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
951
1002
  if (pServerCountAfter < 1)
952
1003
  {
953
1004
  tmpSyncState.UpdateDateSyncDone = true;
1005
+ // No new records -- nothing additional to count
954
1006
  return fStageComplete();
955
1007
  }
956
1008
 
@@ -965,6 +1017,7 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
965
1017
  {
966
1018
  this.fable.log.info(`${this.EntitySchema.TableName}: pulled ${pSyncedCount} new/modified records via UpdateDate.`);
967
1019
  }
1020
+ this._incrementProgress(pSyncedCount || 0);
968
1021
  tmpSyncState.UpdateDateSyncDone = true;
969
1022
  return fStageComplete();
970
1023
  });
@@ -1047,6 +1100,7 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
1047
1100
  {
1048
1101
  this.fable.log.info(`${this.EntitySchema.TableName}: pulled ${pSyncedCount} new records by ID.`);
1049
1102
  }
1103
+ this._incrementProgress(pSyncedCount || 0);
1050
1104
  return fStageComplete();
1051
1105
  });
1052
1106
  },
@@ -1058,6 +1112,13 @@ class MeadowSyncEntityOngoing extends libFableServiceProviderBase
1058
1112
  this.fable.log.error(`Error performing ongoing sync ${this.EntitySchema.TableName}: ${pError}`, { Error: pError });
1059
1113
  }
1060
1114
 
1115
+ // Mark progress tracker as complete so the UI shows the correct totals
1116
+ let tmpTracker = this.operation.progressTrackers[`FullSync-${this.EntitySchema.TableName}`];
1117
+ if (tmpTracker)
1118
+ {
1119
+ tmpTracker.CurrentCount = tmpTracker.TotalCount;
1120
+ }
1121
+
1061
1122
  this.fable.log.info(`${this.EntitySchema.TableName}: ongoing sync complete.`);
1062
1123
 
1063
1124
  if (this.SyncDeletedRecords)
@@ -65,6 +65,18 @@ class MeadowSync extends libFableServiceProviderBase
65
65
  this.MaxRecordsPerEntity = parseInt(this.options.MaxRecordsPerEntity, 10) || 0;
66
66
  }
67
67
 
68
+ // Tolerance window in milliseconds for cross-database timestamp precision differences.
69
+ // Passed through to Ongoing sync entities for bisection date comparison.
70
+ this.DateTimePrecisionMS = 1000;
71
+ if (this.fable.ProgramConfiguration.hasOwnProperty('DateTimePrecisionMS'))
72
+ {
73
+ this.DateTimePrecisionMS = parseInt(this.fable.ProgramConfiguration.DateTimePrecisionMS, 10) || 1000;
74
+ }
75
+ else if (this.options.hasOwnProperty('DateTimePrecisionMS'))
76
+ {
77
+ this.DateTimePrecisionMS = parseInt(this.options.DateTimePrecisionMS, 10) || 1000;
78
+ }
79
+
68
80
  this.MeadowSchema = false;
69
81
  this.MeadowSchemaTableList = false;
70
82
 
@@ -101,6 +113,7 @@ class MeadowSync extends libFableServiceProviderBase
101
113
  PageSize: this.options.PageSize || 100,
102
114
  SyncDeletedRecords: this.SyncDeletedRecords,
103
115
  MaxRecordsPerEntity: this.MaxRecordsPerEntity,
116
+ DateTimePrecisionMS: this.DateTimePrecisionMS,
104
117
  };
105
118
 
106
119
  let tmpSyncEntity;