retold-data-service 2.0.33 → 2.0.34

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.
@@ -3315,7 +3315,7 @@
3315
3315
  12: [function (require, module, exports) {
3316
3316
  module.exports = {
3317
3317
  "name": "pict-view",
3318
- "version": "1.0.67",
3318
+ "version": "1.0.68",
3319
3319
  "description": "Pict View Base Class",
3320
3320
  "main": "source/Pict-View.js",
3321
3321
  "scripts": {
@@ -3345,8 +3345,8 @@
3345
3345
  "@eslint/js": "^9.39.1",
3346
3346
  "browser-env": "^3.3.0",
3347
3347
  "eslint": "^9.39.1",
3348
- "pict": "^1.0.348",
3349
- "quackage": "^1.0.58",
3348
+ "pict": "^1.0.363",
3349
+ "quackage": "^1.0.65",
3350
3350
  "typescript": "^5.9.3"
3351
3351
  },
3352
3352
  "mocha": {
@@ -3361,7 +3361,7 @@
3361
3361
  "watch-ignore": ["lib/vendor"]
3362
3362
  },
3363
3363
  "dependencies": {
3364
- "fable": "^3.1.63",
3364
+ "fable": "^3.1.67",
3365
3365
  "fable-serviceproviderbase": "^3.0.19"
3366
3366
  }
3367
3367
  };
@@ -4139,6 +4139,10 @@
4139
4139
  // Execute the developer-overridable post-render behavior
4140
4140
  tmpView.onAfterRender(tmpEvent.Data.Renderable);
4141
4141
  }
4142
+ // Queue is drained and nested child renders have each cleaned up
4143
+ // their own transactions; remove this root render's entry from
4144
+ // the tracking map so it does not leak.
4145
+ this.pict.TransactionTracking.unregisterTransaction(pRenderable.TransactionHash);
4142
4146
  }
4143
4147
  return true;
4144
4148
  }
@@ -4150,9 +4154,17 @@
4150
4154
  * @param {Renderable} pRenderable - The renderable that was rendered.
4151
4155
  */
4152
4156
  onAfterRenderAsync(fCallback, pRenderable) {
4157
+ // NOTE: this.onAfterRender(pRenderable) will itself clear the
4158
+ // transaction queue and unregister the transaction if this view is
4159
+ // the root renderable - see onAfterRender above. So by the time the
4160
+ // loop below runs, the queue is already empty and there is nothing
4161
+ // to drain. Keeping the async queue walk here defensively in case
4162
+ // future subclasses override onAfterRender in ways that skip the
4163
+ // drain, but the common path is now "sync drain, async no-op".
4153
4164
  this.onAfterRender(pRenderable);
4154
4165
  const tmpAnticipate = this.fable.newAnticipate();
4155
- if (pRenderable && pRenderable.RootRenderableViewHash === this.Hash) {
4166
+ const tmpIsRootRenderable = pRenderable && pRenderable.RootRenderableViewHash === this.Hash;
4167
+ if (tmpIsRootRenderable) {
4156
4168
  const queue = this.pict.TransactionTracking.clearTransactionQueue(pRenderable.TransactionHash) || [];
4157
4169
  for (const event of queue) {
4158
4170
  /** @type {PictView} */
@@ -4169,7 +4181,18 @@
4169
4181
  // Execute the developer-overridable post-render behavior
4170
4182
  }
4171
4183
  }
4172
- return tmpAnticipate.wait(fCallback);
4184
+ return tmpAnticipate.wait(pError => {
4185
+ // Nested virtual-assignment children have now settled their own
4186
+ // onAfterRenderAsync chains (and unregistered their own
4187
+ // transactions along the way). Ensure this root render's entry
4188
+ // is also gone - unregisterTransaction is a no-op if the sync
4189
+ // onAfterRender above already removed it, so this is safe to
4190
+ // call unconditionally on the root path.
4191
+ if (tmpIsRootRenderable && pRenderable && pRenderable.TransactionHash) {
4192
+ this.pict.TransactionTracking.unregisterTransaction(pRenderable.TransactionHash);
4193
+ }
4194
+ return fCallback(pError);
4195
+ });
4173
4196
  }
4174
4197
 
4175
4198
  /**
@@ -7513,6 +7536,21 @@ select { background: #fff; width: 100%; padding: 8px 12px; border: 1px solid #cc
7513
7536
  constructor(pFable, pOptions, pServiceHash) {
7514
7537
  super(pFable, pOptions, pServiceHash);
7515
7538
  }
7539
+ onSyncModeChanged() {
7540
+ let tmpMode = document.querySelector('input[name="syncMode"]:checked').value;
7541
+
7542
+ // Hide all mode-specific option panels
7543
+ let tmpPanels = document.querySelectorAll('[id^="syncModeOptions-"]');
7544
+ for (let i = 0; i < tmpPanels.length; i++) {
7545
+ tmpPanels[i].style.display = 'none';
7546
+ }
7547
+
7548
+ // Show the panel for the selected mode (if one exists)
7549
+ let tmpPanel = document.getElementById('syncModeOptions-' + tmpMode);
7550
+ if (tmpPanel) {
7551
+ tmpPanel.style.display = 'block';
7552
+ }
7553
+ }
7516
7554
  startSync() {
7517
7555
  let tmpSelectedTables = this.pict.views['DataCloner-Schema'].getSelectedTables();
7518
7556
  let tmpPageSize = parseInt(document.getElementById('pageSize').value, 10) || 100;
@@ -7541,6 +7579,16 @@ select { background: #fff; width: 100%; padding: 8px 12px; border: 1px solid #cc
7541
7579
  if (tmpMaxRecords > 0) tmpPostBody.MaxRecordsPerEntity = tmpMaxRecords;
7542
7580
  if (tmpLogToFile) tmpPostBody.LogToFile = true;
7543
7581
  if (tmpAdvancedIDPagination) tmpPostBody.UseAdvancedIDPagination = true;
7582
+
7583
+ // Strategy-specific options
7584
+ if (tmpSyncMode === 'OngoingEventualConsistency') {
7585
+ let tmpBackSyncTimeLimit = parseInt(document.getElementById('backSyncTimeLimit').value, 10);
7586
+ if (tmpBackSyncTimeLimit > 0) tmpPostBody.BackSyncTimeLimit = tmpBackSyncTimeLimit;
7587
+ }
7588
+ if (tmpSyncMode === 'TrueUp') {
7589
+ let tmpTrueUpPageSize = parseInt(document.getElementById('trueUpPageSize').value, 10);
7590
+ if (tmpTrueUpPageSize > 0) tmpPostBody.TrueUpPageSize = tmpTrueUpPageSize;
7591
+ }
7544
7592
  this.pict.providers.DataCloner.api('POST', '/clone/sync/start', tmpPostBody).then(function (pData) {
7545
7593
  if (pData.Success) {
7546
7594
  let tmpMsg = pData.SyncMode + ' sync started for ' + pData.Tables.length + ' tables.';
@@ -7914,16 +7962,48 @@ select { background: #fff; width: 100%; padding: 8px 12px; border: 1px solid #cc
7914
7962
 
7915
7963
  <div style="margin-bottom:10px">
7916
7964
  <label style="margin-bottom:6px">Sync Mode</label>
7917
- <div style="display:flex; gap:16px; align-items:center">
7965
+ <div style="display:flex; gap:12px 20px; align-items:center; flex-wrap:wrap">
7918
7966
  <label style="font-weight:normal; margin:0; cursor:pointer">
7919
- <input type="radio" name="syncMode" id="syncModeInitial" value="Initial" checked> Initial
7920
- <span style="color:#888; font-size:0.85em">(full clone — download all records)</span>
7967
+ <input type="radio" name="syncMode" id="syncModeInitial" value="Initial" checked onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> Initial
7968
+ <span style="color:#888; font-size:0.85em">(full clone)</span>
7921
7969
  </label>
7922
7970
  <label style="font-weight:normal; margin:0; cursor:pointer">
7923
- <input type="radio" name="syncMode" id="syncModeOngoing" value="Ongoing"> Ongoing
7924
- <span style="color:#888; font-size:0.85em">(delta — only new/updated records since last sync)</span>
7971
+ <input type="radio" name="syncMode" id="syncModeOngoing" value="Ongoing" onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> Ongoing
7972
+ <span style="color:#888; font-size:0.85em">(delta sync)</span>
7925
7973
  </label>
7974
+ <label style="font-weight:normal; margin:0; cursor:pointer">
7975
+ <input type="radio" name="syncMode" id="syncModeOngoingEventualConsistency" value="OngoingEventualConsistency" onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> Eventual
7976
+ <span style="color:#888; font-size:0.85em">(time-budgeted back-sync)</span>
7977
+ </label>
7978
+ <label style="font-weight:normal; margin:0; cursor:pointer">
7979
+ <input type="radio" name="syncMode" id="syncModeTrueUp" value="TrueUp" onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> True-Up
7980
+ <span style="color:#888; font-size:0.85em">(linear walk, one-time)</span>
7981
+ </label>
7982
+ <label style="font-weight:normal; margin:0; cursor:pointer">
7983
+ <input type="radio" name="syncMode" id="syncModeComparisonOnly" value="ComparisonOnly" onchange="pict.views['DataCloner-Sync'].onSyncModeChanged()"> Compare
7984
+ <span style="color:#888; font-size:0.85em">(diff report, no sync)</span>
7985
+ </label>
7986
+ </div>
7987
+ </div>
7988
+
7989
+ <div id="syncModeOptions-OngoingEventualConsistency" style="display:none; margin-bottom:10px">
7990
+ <div style="display:flex; gap:8px; align-items:flex-end">
7991
+ <div style="flex:0 0 220px">
7992
+ <label for="backSyncTimeLimit">Back-Sync Time Budget (ms)</label>
7993
+ <input type="number" id="backSyncTimeLimit" value="30000" min="1000" max="600000" style="margin-bottom:0">
7994
+ </div>
7995
+ </div>
7996
+ <div style="font-size:0.8em; color:#888; padding-left:4px">Milliseconds devoted to backwards bisection per entity before pulling new records (default: 30000)</div>
7997
+ </div>
7998
+
7999
+ <div id="syncModeOptions-TrueUp" style="display:none; margin-bottom:10px">
8000
+ <div style="display:flex; gap:8px; align-items:flex-end">
8001
+ <div style="flex:0 0 220px">
8002
+ <label for="trueUpPageSize">True-Up Page Size</label>
8003
+ <input type="number" id="trueUpPageSize" value="500" min="10" max="10000" style="margin-bottom:0">
8004
+ </div>
7926
8005
  </div>
8006
+ <div style="font-size:0.8em; color:#888; padding-left:4px">Records per page for the linear keyset walk (default: 500)</div>
7927
8007
  </div>
7928
8008
 
7929
8009
  <div class="checkbox-row">