retold-data-service 2.0.16 → 2.0.18

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.
Files changed (28) hide show
  1. package/.claude/launch.json +2 -2
  2. package/.quackage.json +19 -0
  3. package/package.json +13 -6
  4. package/source/services/data-cloner/DataCloner-Command-Sync.js +83 -50
  5. package/source/services/data-cloner/DataCloner-Command-WebUI.js +27 -10
  6. package/source/services/data-cloner/Retold-Data-Service-DataCloner.js +281 -4
  7. package/source/services/data-cloner/pict-app/Pict-Application-DataCloner-Configuration.json +9 -0
  8. package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +102 -0
  9. package/source/services/data-cloner/pict-app/Pict-DataCloner-Bundle.js +6 -0
  10. package/source/services/data-cloner/pict-app/providers/Pict-Provider-DataCloner.js +998 -0
  11. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Connection.js +407 -0
  12. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Deploy.js +126 -0
  13. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +483 -0
  14. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Layout.js +390 -0
  15. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Schema.js +241 -0
  16. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Session.js +268 -0
  17. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Sync.js +575 -0
  18. package/source/services/data-cloner/pict-app/views/PictView-DataCloner-ViewData.js +176 -0
  19. package/source/services/data-cloner/web/data-cloner.js +7952 -0
  20. package/source/services/data-cloner/web/data-cloner.js.map +1 -0
  21. package/source/services/data-cloner/web/data-cloner.min.js +2 -0
  22. package/source/services/data-cloner/web/data-cloner.min.js.map +1 -0
  23. package/source/services/data-cloner/web/index.html +17 -0
  24. package/test/DataCloner-Integration_tests.js +1205 -0
  25. package/test/DataCloner-Puppeteer_tests.js +502 -0
  26. package/test/integration-report.json +311 -0
  27. package/test/run-integration-tests.js +501 -0
  28. package/source/services/data-cloner/data-cloner-web.html +0 -2706
@@ -77,7 +77,11 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
77
77
  SyncStartTime: null,
78
78
  SyncEndTime: null,
79
79
  SyncEventLog: [],
80
- SyncReport: null
80
+ SyncReport: null,
81
+
82
+ // Throughput sampling — records per 10-second window
83
+ ThroughputSamples: [],
84
+ ThroughputTimer: null
81
85
  });
82
86
 
83
87
  // Create an isolated Pict instance for remote session management
@@ -156,8 +160,40 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
156
160
  return fCallback(new Error(`Could not load module "${tmpRegistryEntry.moduleName}": ${pRequireError.message}. Run: npm install ${tmpRegistryEntry.moduleName}`));
157
161
  }
158
162
 
163
+ // Normalize the config to include both old-style (Server, Port, User, etc.)
164
+ // and mysql2-native (host, port, user, etc.) property names so it works
165
+ // with any version of the meadow-connection provider.
166
+ let tmpNormalizedConfig = Object.assign({}, pConfig);
167
+ if (pProviderName === 'MySQL' || pProviderName === 'MSSQL' || pProviderName === 'PostgreSQL')
168
+ {
169
+ if (tmpNormalizedConfig.host && !tmpNormalizedConfig.Server)
170
+ {
171
+ tmpNormalizedConfig.Server = tmpNormalizedConfig.host;
172
+ }
173
+ if (tmpNormalizedConfig.port && !tmpNormalizedConfig.Port)
174
+ {
175
+ tmpNormalizedConfig.Port = tmpNormalizedConfig.port;
176
+ }
177
+ if (tmpNormalizedConfig.user && !tmpNormalizedConfig.User)
178
+ {
179
+ tmpNormalizedConfig.User = tmpNormalizedConfig.user;
180
+ }
181
+ if (tmpNormalizedConfig.password && !tmpNormalizedConfig.Password)
182
+ {
183
+ tmpNormalizedConfig.Password = tmpNormalizedConfig.password;
184
+ }
185
+ if (tmpNormalizedConfig.database && !tmpNormalizedConfig.Database)
186
+ {
187
+ tmpNormalizedConfig.Database = tmpNormalizedConfig.database;
188
+ }
189
+ if (tmpNormalizedConfig.connectionLimit && !tmpNormalizedConfig.ConnectionPoolLimit)
190
+ {
191
+ tmpNormalizedConfig.ConnectionPoolLimit = tmpNormalizedConfig.connectionLimit;
192
+ }
193
+ }
194
+
159
195
  // Set the provider configuration on fable settings
160
- this.fable.settings[tmpRegistryEntry.configKey] = pConfig;
196
+ this.fable.settings[tmpRegistryEntry.configKey] = tmpNormalizedConfig;
161
197
 
162
198
  // Register and instantiate the provider if not already present
163
199
  if (!this.fable[tmpRegistryEntry.serviceName])
@@ -165,6 +201,12 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
165
201
  this.fable.serviceManager.addServiceType(tmpRegistryEntry.serviceName, tmpModule);
166
202
  this.fable.serviceManager.instantiateServiceProvider(tmpRegistryEntry.serviceName);
167
203
  }
204
+ else
205
+ {
206
+ // Provider already exists — update its options with the new config
207
+ // so reconnects use the current settings.
208
+ this.fable[tmpRegistryEntry.serviceName].options[tmpRegistryEntry.configKey] = tmpNormalizedConfig;
209
+ }
168
210
 
169
211
  this.fable[tmpRegistryEntry.serviceName].connectAsync(
170
212
  (pError) =>
@@ -322,6 +364,12 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
322
364
  Synced: tmpP.Synced || 0,
323
365
  Skipped: tmpP.Skipped || 0,
324
366
  Errors: tmpP.Errors || 0,
367
+ New: tmpP.New || 0,
368
+ Updated: tmpP.Updated || 0,
369
+ Unchanged: tmpP.Unchanged || 0,
370
+ Deleted: tmpP.Deleted || 0,
371
+ ServerTotal: tmpP.ServerTotal || 0,
372
+ LocalCountBefore: tmpP.LocalCountBefore || 0,
325
373
  ErrorMessage: tmpP.ErrorMessage || null,
326
374
  StartTime: tmpP.StartTime || null,
327
375
  EndTime: tmpP.EndTime || null,
@@ -434,7 +482,8 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
434
482
  },
435
483
  Tables: tmpTables,
436
484
  Anomalies: tmpAnomalies,
437
- EventLog: tmpState.SyncEventLog
485
+ EventLog: tmpState.SyncEventLog,
486
+ ThroughputSamples: tmpState.ThroughputSamples || []
438
487
  });
439
488
 
440
489
  tmpState.SyncReport = tmpReport;
@@ -513,6 +562,75 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
513
562
  this.fable.log.info(`\n${tmpLines.join('\n')}`);
514
563
  }
515
564
 
565
+
566
+ /**
567
+ * Start the throughput sampler. Every 10 seconds, records a cumulative
568
+ * { t, synced } sample so the UI can render a records-per-10s histogram.
569
+ */
570
+ startThroughputSampling()
571
+ {
572
+ this.stopThroughputSampling();
573
+ this._cloneState.ThroughputSamples = [];
574
+ this._cloneState.ThroughputSamples.push({ t: Date.now(), synced: 0 });
575
+
576
+ this._cloneState.ThroughputTimer = setInterval(
577
+ () =>
578
+ {
579
+ // Read directly from MeadowSync progress trackers for accurate
580
+ // real-time counts, then update SyncProgress as a side-effect so
581
+ // the next live-status poll also has fresh data.
582
+ let tmpTotalSynced = 0;
583
+ let tmpTableNames = Object.keys(this._cloneState.SyncProgress);
584
+ let tmpSyncEntities = (this.fable.MeadowSync && this.fable.MeadowSync.MeadowSyncEntities) || {};
585
+
586
+ for (let i = 0; i < tmpTableNames.length; i++)
587
+ {
588
+ let tmpName = tmpTableNames[i];
589
+ let tmpProgress = this._cloneState.SyncProgress[tmpName];
590
+
591
+ // If the entity is actively syncing, read the live tracker value
592
+ if (tmpProgress.Status === 'Syncing' || tmpProgress.Status === 'Pending')
593
+ {
594
+ let tmpSyncEntity = tmpSyncEntities[tmpName];
595
+ if (tmpSyncEntity && tmpSyncEntity.operation)
596
+ {
597
+ let tmpTracker = tmpSyncEntity.operation.progressTrackers[`FullSync-${tmpName}`];
598
+ if (tmpTracker)
599
+ {
600
+ tmpProgress.Total = tmpTracker.TotalCount || tmpProgress.Total;
601
+ tmpProgress.Synced = Math.max(tmpTracker.CurrentCount || 0, 0);
602
+ }
603
+ }
604
+ }
605
+
606
+ tmpTotalSynced += (tmpProgress.Synced || 0);
607
+ }
608
+ this._cloneState.ThroughputSamples.push({ t: Date.now(), synced: tmpTotalSynced });
609
+ }, 10000);
610
+ }
611
+
612
+ /**
613
+ * Stop the throughput sampler and record a final sample.
614
+ */
615
+ stopThroughputSampling()
616
+ {
617
+ if (this._cloneState.ThroughputTimer)
618
+ {
619
+ clearInterval(this._cloneState.ThroughputTimer);
620
+ this._cloneState.ThroughputTimer = null;
621
+ }
622
+ if (this._cloneState.ThroughputSamples && this._cloneState.ThroughputSamples.length > 0)
623
+ {
624
+ let tmpTotalSynced = 0;
625
+ let tmpTableNames = Object.keys(this._cloneState.SyncProgress);
626
+ for (let i = 0; i < tmpTableNames.length; i++)
627
+ {
628
+ tmpTotalSynced += (this._cloneState.SyncProgress[tmpTableNames[i]].Synced || 0);
629
+ }
630
+ this._cloneState.ThroughputSamples.push({ t: Date.now(), synced: tmpTotalSynced });
631
+ }
632
+ }
633
+
516
634
  /**
517
635
  * Pre-count phase — fetch record counts for all tables in parallel
518
636
  * before starting the actual sync. Populates SyncProgress[table].Total
@@ -565,6 +683,128 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
565
683
  });
566
684
  }
567
685
 
686
+ /**
687
+ * Warm up the local database connection by running a lightweight test query.
688
+ * This forces the connection pool to recycle any stale/dead connections
689
+ * (e.g. after a burst of CREATE TABLE statements during deploy).
690
+ * Retries up to 3 times with a short delay between attempts.
691
+ *
692
+ * @param {Function} fCallback - Called when a connection is verified
693
+ */
694
+ warmUpLocalConnection(fCallback)
695
+ {
696
+ let tmpProviderName = this._cloneState.ConnectionProvider;
697
+ let tmpAttempts = 0;
698
+ let tmpMaxAttempts = 5;
699
+ let tmpRetryDelayMs = 2000;
700
+
701
+ let fAttempt = () =>
702
+ {
703
+ tmpAttempts++;
704
+
705
+ if (tmpProviderName === 'SQLite')
706
+ {
707
+ // SQLite doesn't have connection pool issues
708
+ return fCallback();
709
+ }
710
+
711
+ // Look up the provider service via the registry
712
+ let tmpRegistryEntry = _ProviderRegistry[tmpProviderName];
713
+ if (!tmpRegistryEntry)
714
+ {
715
+ this.fable.log.warn(`Data Cloner: Connection warmup — unknown provider "${tmpProviderName}", skipping.`);
716
+ return fCallback();
717
+ }
718
+
719
+ let tmpProviderService = this.fable[tmpRegistryEntry.serviceName];
720
+ if (!tmpProviderService)
721
+ {
722
+ this.fable.log.warn(`Data Cloner: Connection warmup — provider service "${tmpRegistryEntry.serviceName}" not available, skipping.`);
723
+ return fCallback();
724
+ }
725
+
726
+ // Log the pool config on the first attempt for diagnostics
727
+ if (tmpAttempts === 1)
728
+ {
729
+ let tmpPoolConfig = tmpProviderService.options && tmpProviderService.options.MySQL;
730
+ if (tmpPoolConfig)
731
+ {
732
+ this.fable.log.info(`Data Cloner: Connection warmup — pool target: ${tmpPoolConfig.host}:${tmpPoolConfig.port} database=${tmpPoolConfig.database} user=${tmpPoolConfig.user}`);
733
+ }
734
+ }
735
+
736
+ // If the pool doesn't exist or a previous attempt failed, try to (re)create it
737
+ if (!tmpProviderService.pool)
738
+ {
739
+ this.fable.log.info(`Data Cloner: Connection warmup — no pool exists, calling connect()...`);
740
+ try
741
+ {
742
+ tmpProviderService.connect();
743
+ }
744
+ catch (pConnectError)
745
+ {
746
+ this.fable.log.warn(`Data Cloner: Connection warmup — connect() error: ${pConnectError.message}`);
747
+ }
748
+ }
749
+
750
+ if (!tmpProviderService.pool)
751
+ {
752
+ this.fable.log.warn(`Data Cloner: Connection warmup — pool still not available after connect(), skipping.`);
753
+ return fCallback();
754
+ }
755
+
756
+ // Run a lightweight test query to force the pool to recycle stale connections
757
+ this.fable.log.info(`Data Cloner: Connection warmup attempt ${tmpAttempts}/${tmpMaxAttempts}...`);
758
+ tmpProviderService.pool.query('SELECT 1 AS _warmup',
759
+ (pError) =>
760
+ {
761
+ if (pError)
762
+ {
763
+ this.fable.log.warn(`Data Cloner: Connection warmup attempt ${tmpAttempts}/${tmpMaxAttempts} failed: ${pError.code || pError.message}`);
764
+
765
+ // If the pool is persistently failing, destroy and recreate it
766
+ if (tmpAttempts >= 2 && pError.code === 'ECONNREFUSED')
767
+ {
768
+ this.fable.log.info('Data Cloner: Connection warmup — destroying stale pool and recreating...');
769
+ try
770
+ {
771
+ tmpProviderService.pool.end(() => {});
772
+ }
773
+ catch (pEndError) { /* ignore */ }
774
+ tmpProviderService._ConnectionPool = false;
775
+ tmpProviderService.connected = false;
776
+ try
777
+ {
778
+ tmpProviderService.connect();
779
+ this.fable.log.info('Data Cloner: Connection warmup — pool recreated.');
780
+ }
781
+ catch (pReconnectError)
782
+ {
783
+ this.fable.log.warn(`Data Cloner: Connection warmup — reconnect error: ${pReconnectError.message}`);
784
+ }
785
+ }
786
+
787
+ if (tmpAttempts < tmpMaxAttempts)
788
+ {
789
+ setTimeout(fAttempt, tmpRetryDelayMs);
790
+ }
791
+ else
792
+ {
793
+ this.fable.log.warn('Data Cloner: Connection warmup exhausted retries — proceeding with sync anyway.');
794
+ return fCallback();
795
+ }
796
+ }
797
+ else
798
+ {
799
+ this.fable.log.info(`Data Cloner: Connection warmup succeeded on attempt ${tmpAttempts}.`);
800
+ return fCallback();
801
+ }
802
+ });
803
+ };
804
+
805
+ fAttempt();
806
+ }
807
+
568
808
  /**
569
809
  * The sync engine — synchronize data for a list of tables sequentially.
570
810
  *
@@ -591,10 +831,12 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
591
831
  SyncDeletedRecords: this._cloneState.SyncDeletedRecords
592
832
  });
593
833
 
834
+ this.startThroughputSampling();
594
835
  let fSyncNextTable = () =>
595
836
  {
596
837
  if (this._cloneState.SyncStopping || tmpTableIndex >= pTables.length)
597
838
  {
839
+ this.stopThroughputSampling();
598
840
  this._cloneState.SyncRunning = false;
599
841
  this._cloneState.SyncEndTime = new Date().toJSON();
600
842
 
@@ -619,6 +861,22 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
619
861
  }
620
862
 
621
863
  this.fable.log.info('Data Cloner: Sync complete.');
864
+
865
+ // Close the log file stream if one was opened for this run
866
+ if (this._cloneState.SyncLogFileLogger)
867
+ {
868
+ let tmpLogPath = this._cloneState.SyncLogFilePath || '';
869
+ this._cloneState.SyncLogFileLogger.flushBufferToLogFile(() =>
870
+ {
871
+ this._cloneState.SyncLogFileLogger.closeWriter(() =>
872
+ {
873
+ this.fable.log.info(`Data Cloner: Log file closed — ${tmpLogPath}`);
874
+ });
875
+ });
876
+ this._cloneState.SyncLogFileLogger = null;
877
+ this._cloneState.SyncLogFilePath = null;
878
+ }
879
+
622
880
  return;
623
881
  }
624
882
 
@@ -652,6 +910,18 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
652
910
  }
653
911
  }
654
912
 
913
+ // Read per-record breakdown from the sync entity
914
+ if (tmpSyncEntity && tmpSyncEntity.syncResults)
915
+ {
916
+ let tmpResults = tmpSyncEntity.syncResults;
917
+ tmpProgress.New = tmpResults.Created || 0;
918
+ tmpProgress.Updated = 0;
919
+ tmpProgress.Unchanged = tmpResults.LocalRecordCount || 0;
920
+ tmpProgress.Deleted = tmpResults.Deleted || 0;
921
+ tmpProgress.ServerTotal = tmpResults.ServerRecordCount || 0;
922
+ tmpProgress.LocalCountBefore = tmpResults.LocalRecordCount || 0;
923
+ }
924
+
655
925
  let tmpRESTErrors = this._cloneState.SyncRESTErrors[tmpTableName] || 0;
656
926
  tmpProgress.Errors = tmpRESTErrors;
657
927
 
@@ -698,7 +968,14 @@ class RetoldDataServiceDataCloner extends libFableServiceProviderBase
698
968
  this.preCountTables(pTables,
699
969
  () =>
700
970
  {
701
- fSyncNextTable();
971
+ // Warm up the local database connection before starting sync.
972
+ // After the pre-count phase (which only queries the remote server),
973
+ // local DB pool connections may have gone stale. A test query
974
+ // forces the pool to recycle dead connections before we sync.
975
+ this.warmUpLocalConnection(() =>
976
+ {
977
+ fSyncNextTable();
978
+ });
702
979
  });
703
980
  }
704
981
 
@@ -0,0 +1,9 @@
1
+ {
2
+ "Name": "Retold Data Cloner",
3
+ "Hash": "DataCloner",
4
+ "MainViewportViewIdentifier": "DataCloner-Layout",
5
+ "MainViewportDestinationAddress": "#DataCloner-Application-Container",
6
+ "MainViewportDefaultDataAddress": "AppData.DataCloner",
7
+ "pict_configuration": { "Product": "DataCloner" },
8
+ "AutoRenderMainViewportViewAfterInitialize": false
9
+ }
@@ -0,0 +1,102 @@
1
+ const libPictApplication = require('pict-application');
2
+
3
+ const libProvider = require('./providers/Pict-Provider-DataCloner.js');
4
+
5
+ const libViewLayout = require('./views/PictView-DataCloner-Layout.js');
6
+ const libViewConnection = require('./views/PictView-DataCloner-Connection.js');
7
+ const libViewSession = require('./views/PictView-DataCloner-Session.js');
8
+ const libViewSchema = require('./views/PictView-DataCloner-Schema.js');
9
+ const libViewDeploy = require('./views/PictView-DataCloner-Deploy.js');
10
+ const libViewSync = require('./views/PictView-DataCloner-Sync.js');
11
+ const libViewExport = require('./views/PictView-DataCloner-Export.js');
12
+ const libViewViewData = require('./views/PictView-DataCloner-ViewData.js');
13
+ const libViewHistogram = require('pict-section-histogram');
14
+
15
+ class DataClonerApplication extends libPictApplication
16
+ {
17
+ constructor(pFable, pOptions, pServiceHash)
18
+ {
19
+ super(pFable, pOptions, pServiceHash);
20
+
21
+ // Register provider
22
+ this.pict.addProvider('DataCloner', libProvider.default_configuration, libProvider);
23
+
24
+ // Register views
25
+ this.pict.addView('DataCloner-Layout', libViewLayout.default_configuration, libViewLayout);
26
+ this.pict.addView('DataCloner-Connection', libViewConnection.default_configuration, libViewConnection);
27
+ this.pict.addView('DataCloner-Session', libViewSession.default_configuration, libViewSession);
28
+ this.pict.addView('DataCloner-Schema', libViewSchema.default_configuration, libViewSchema);
29
+ this.pict.addView('DataCloner-Deploy', libViewDeploy.default_configuration, libViewDeploy);
30
+ this.pict.addView('DataCloner-Sync', libViewSync.default_configuration, libViewSync);
31
+ this.pict.addView('DataCloner-Export', libViewExport.default_configuration, libViewExport);
32
+ this.pict.addView('DataCloner-ViewData', libViewViewData.default_configuration, libViewViewData);
33
+ this.pict.addView('DataCloner-StatusHistogram',
34
+ {
35
+ ViewIdentifier: 'DataCloner-StatusHistogram',
36
+ TargetElementAddress: '#DataCloner-Throughput-Histogram',
37
+ DefaultDestinationAddress: '#DataCloner-Throughput-Histogram',
38
+ RenderOnLoad: false,
39
+ Selectable: false,
40
+ Orientation: 'vertical',
41
+ FillContainer: true,
42
+ ShowValues: false,
43
+ ShowLabels: true,
44
+ MaxBarSize: 80,
45
+ BarColor: '#4a90d9',
46
+ Bins: []
47
+ }, libViewHistogram);
48
+ }
49
+
50
+ onAfterInitializeAsync(fCallback)
51
+ {
52
+ // Centralized state (replaces global variables)
53
+ this.pict.AppData.DataCloner =
54
+ {
55
+ FetchedTables: [],
56
+ DeployedTables: [],
57
+ LastReport: null,
58
+ ServerBusyAtLoad: false,
59
+ SyncPollTimer: null,
60
+ LiveStatusTimer: null,
61
+ StatusDetailExpanded: false,
62
+ StatusDetailTimer: null,
63
+ StatusDetailData: null,
64
+ LastLiveStatus: null,
65
+ PersistFields: [
66
+ 'serverURL', 'authMethod', 'authURI', 'checkURI',
67
+ 'cookieName', 'cookieValueAddr', 'cookieValueTemplate', 'loginMarker',
68
+ 'userName', 'password', 'schemaURL', 'pageSize', 'dateTimePrecisionMS',
69
+ 'connProvider', 'sqliteFilePath',
70
+ 'mysqlServer', 'mysqlPort', 'mysqlUser', 'mysqlPassword', 'mysqlDatabase', 'mysqlConnectionLimit',
71
+ 'mssqlServer', 'mssqlPort', 'mssqlUser', 'mssqlPassword', 'mssqlDatabase', 'mssqlConnectionLimit',
72
+ 'postgresqlHost', 'postgresqlPort', 'postgresqlUser', 'postgresqlPassword', 'postgresqlDatabase', 'postgresqlConnectionLimit',
73
+ 'solrHost', 'solrPort', 'solrCore', 'solrPath',
74
+ 'mongodbHost', 'mongodbPort', 'mongodbUser', 'mongodbPassword', 'mongodbDatabase', 'mongodbConnectionLimit',
75
+ 'rocksdbFolder',
76
+ 'bibliographFolder'
77
+ ]
78
+ };
79
+
80
+ // Make pict available for inline onclick handlers
81
+ window.pict = this.pict;
82
+
83
+ // Render layout (which chains child view renders via onAfterRender)
84
+ this.pict.views['DataCloner-Layout'].render();
85
+
86
+ // Post-render initialization
87
+ this.pict.providers.DataCloner.initPersistence();
88
+ this.pict.views['DataCloner-Connection'].onProviderChange();
89
+ this.pict.providers.DataCloner.restoreDeployedTables();
90
+ this.pict.providers.DataCloner.startLiveStatusPolling();
91
+ this.pict.providers.DataCloner.initAccordionPreviews();
92
+ this.pict.providers.DataCloner.updateAllPreviews();
93
+ this.pict.views['DataCloner-Layout'].collapseAllSections();
94
+ this.pict.providers.DataCloner.initAutoProcess();
95
+
96
+ return fCallback();
97
+ }
98
+ }
99
+
100
+ module.exports = DataClonerApplication;
101
+
102
+ module.exports.default_configuration = require('./Pict-Application-DataCloner-Configuration.json');
@@ -0,0 +1,6 @@
1
+ module.exports = { DataClonerApplication: require('./Pict-Application-DataCloner.js') };
2
+
3
+ if (typeof(window) !== 'undefined')
4
+ {
5
+ window.DataClonerApplication = module.exports.DataClonerApplication;
6
+ }