retold-data-service 2.1.1 → 2.1.5
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/BUILDING-AND-PUBLISHING.md +2 -2
- package/Dockerfile +1 -1
- package/README.md +12 -27
- package/build-all.js +66 -0
- package/diagrams/architecture.excalidraw +2966 -0
- package/diagrams/architecture.mmd +17 -0
- package/diagrams/architecture.svg +2 -0
- package/docs/README.md +12 -12
- package/docs/_brand.json +18 -0
- package/docs/_cover.md +1 -1
- package/docs/_topbar.md +1 -1
- package/docs/_version.json +3 -3
- package/docs/api/reference.md +8 -8
- package/docs/architecture.md +6 -84
- package/docs/diagrams/component-diagram.excalidraw +2807 -0
- package/docs/diagrams/component-diagram.mmd +14 -0
- package/docs/diagrams/component-diagram.svg +2 -0
- package/docs/diagrams/component-stack.excalidraw +1169 -0
- package/docs/diagrams/component-stack.mmd +6 -0
- package/docs/diagrams/component-stack.svg +2 -0
- package/docs/diagrams/hook-execution-order.excalidraw +3230 -0
- package/docs/diagrams/hook-execution-order.mmd +19 -0
- package/docs/diagrams/hook-execution-order.svg +2 -0
- package/docs/diagrams/initialization-flow.excalidraw +1800 -0
- package/docs/diagrams/initialization-flow.mmd +22 -0
- package/docs/diagrams/initialization-flow.svg +2 -0
- package/docs/index.html +6 -7
- package/docs/lifecycle-hooks.md +2 -21
- package/docs/retold-catalog.json +141 -141
- package/docs/retold-keyword-index.json +6818 -1608
- package/package.json +130 -96
- package/source/services/RetoldDataService-Brand.js +13 -0
- package/source/services/comprehension-loader/pict-app/Pict-Application-ComprehensionLoader.js +65 -15
- package/source/services/comprehension-loader/pict-app/providers/Pict-Provider-ComprehensionLoader.js +2 -2
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Layout.js +68 -114
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Load.js +29 -29
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Schema.js +3 -3
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Session.js +2 -2
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-SettingsPanel.js +62 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Shell.js +142 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-Source.js +7 -7
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusBar.js +125 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-StatusDetail.js +89 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-Nav.js +42 -0
- package/source/services/comprehension-loader/pict-app/views/PictView-ComprehensionLoader-TopBar-User.js +48 -0
- package/source/services/comprehension-loader/web/comprehension-loader.js +5475 -6243
- package/source/services/comprehension-loader/web/comprehension-loader.js.map +1 -1
- package/source/services/comprehension-loader/web/comprehension-loader.min.js +75 -1
- package/source/services/comprehension-loader/web/comprehension-loader.min.js.map +1 -1
- package/source/services/comprehension-loader/web/favicons/favicon-dark.svg +13 -0
- package/source/services/comprehension-loader/web/favicons/favicon-light.svg +13 -0
- package/source/services/comprehension-loader/web/favicons/favicon.svg +13 -0
- package/source/services/comprehension-loader/web/index.html +3 -0
- package/source/services/comprehension-loader/web/pict.min.js +12 -0
- package/source/services/data-cloner/DataCloner-Command-Headless.js +2 -1
- package/source/services/data-cloner/DataCloner-Command-Sync.js +110 -75
- package/source/services/data-cloner/pict-app/Pict-Application-DataCloner.js +70 -47
- package/source/services/data-cloner/pict-app/providers/Pict-Provider-DataCloner.js +3 -3
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Connection.js +1 -1
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Deploy.js +5 -5
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Export.js +11 -11
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Layout.js +89 -135
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Schema.js +3 -3
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Session.js +2 -2
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-SettingsPanel.js +61 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Shell.js +136 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusBar.js +117 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-StatusDetail.js +81 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-Sync.js +38 -38
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-Nav.js +42 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-TopBar-User.js +48 -0
- package/source/services/data-cloner/pict-app/views/PictView-DataCloner-ViewData.js +5 -5
- package/source/services/data-cloner/web/data-cloner.js +5855 -8067
- package/source/services/data-cloner/web/data-cloner.js.map +1 -1
- package/source/services/data-cloner/web/data-cloner.min.js +75 -1
- package/source/services/data-cloner/web/data-cloner.min.js.map +1 -1
- package/source/services/data-cloner/web/favicons/favicon-dark.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicon-light.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicon.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicons/favicon-dark.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicons/favicon-light.svg +13 -0
- package/source/services/data-cloner/web/favicons/favicons/favicon.svg +13 -0
- package/source/services/data-cloner/web/index.html +3 -0
- package/source/services/data-cloner/web/pict.min.js +12 -0
- package/test/Bundles_smoke_tests.js +43 -0
- package/test/ComprehensionLoader_smoke_tests.js +95 -0
- package/test/DataCloner-RuntimeOverrides_tests.js +344 -0
- package/test/DataCloner_smoke_tests.js +87 -0
- package/docs/css/docuserve.css +0 -327
|
@@ -253,7 +253,8 @@ module.exports = (pDataClonerService, pConfig, pCLIOptions, fCallback) =>
|
|
|
253
253
|
MaxRecordsPerEntity: tmpMaxRecords || tmpSync.MaxRecordsPerEntity || 0,
|
|
254
254
|
DateTimePrecisionMS: tmpSync.DateTimePrecisionMS,
|
|
255
255
|
BackSyncTimeLimit: tmpSync.BackSyncTimeLimit,
|
|
256
|
-
TrueUpPageSize: tmpSync.TrueUpPageSize
|
|
256
|
+
TrueUpPageSize: tmpSync.TrueUpPageSize,
|
|
257
|
+
SyncEntityOptions: tmpSync.SyncEntityOptions
|
|
257
258
|
});
|
|
258
259
|
|
|
259
260
|
tmpFable.log.info(`Headless: Starting ${tmpSyncBody.SyncMode} sync...`);
|
|
@@ -10,6 +10,109 @@
|
|
|
10
10
|
const libFableLog = require('fable-log');
|
|
11
11
|
const libPath = require('path');
|
|
12
12
|
|
|
13
|
+
// Runtime-overridable sync properties for the /clone/sync/start route.
|
|
14
|
+
// Each entry is a coerce function returning the value to apply, or null
|
|
15
|
+
// if the input is invalid. Drives both the top-level body overrides and
|
|
16
|
+
// the per-entity `SyncEntityOptions` overrides — adding a new knob is a
|
|
17
|
+
// one-line change here.
|
|
18
|
+
const _RuntimeSyncProperties = {
|
|
19
|
+
BackSyncTimeLimit: (pVal) => { let tmpN = parseInt(pVal, 10); return (!isNaN(tmpN) && tmpN > 0) ? tmpN : null; },
|
|
20
|
+
MaxRecordsPerEntity: (pVal) => { let tmpN = parseInt(pVal, 10); return (!isNaN(tmpN) && tmpN > 0) ? tmpN : null; },
|
|
21
|
+
DateTimePrecisionMS: (pVal) => { let tmpN = parseInt(pVal, 10); return !isNaN(tmpN) ? tmpN : null; },
|
|
22
|
+
TrueUpPageSize: (pVal) => { let tmpN = parseInt(pVal, 10); return (!isNaN(tmpN) && tmpN > 0) ? tmpN : null; },
|
|
23
|
+
UseAdvancedIDPagination: (pVal) => !!pVal,
|
|
24
|
+
SyncDeletedRecords: (pVal) => !!pVal
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Apply runtime sync overrides from a request body to a live MeadowSync.
|
|
29
|
+
*
|
|
30
|
+
* Mutates `pMeadowSync.MeadowSyncEntities[*]` properties, plus mirrors the
|
|
31
|
+
* "base" globals onto the MeadowSync orchestrator and `cloneState.SyncDeletedRecords`.
|
|
32
|
+
* Top-level body keys form the base config; per-entity overrides under
|
|
33
|
+
* `pBody.SyncEntityOptions[<TableName>]` are layered on top (per-entity wins).
|
|
34
|
+
*
|
|
35
|
+
* Only properties listed in `_RuntimeSyncProperties` are honored; unknown keys
|
|
36
|
+
* and invalid values are silently ignored. Unknown entity names produce a warn.
|
|
37
|
+
*
|
|
38
|
+
* @param {Object} pBody - Request body (or any plain object with the same shape).
|
|
39
|
+
* @param {Object} pMeadowSync - MeadowSync orchestrator instance with `MeadowSyncEntities`.
|
|
40
|
+
* @param {Object} pCloneState - Clone-state object whose `SyncDeletedRecords` mirrors the base.
|
|
41
|
+
* @param {Object} pLog - Logger with `info(msg)` and `warn(msg)`.
|
|
42
|
+
*/
|
|
43
|
+
const applyRuntimeSyncOverrides = (pBody, pMeadowSync, pCloneState, pLog) =>
|
|
44
|
+
{
|
|
45
|
+
// Compute the base runtime config from top-level body keys.
|
|
46
|
+
let tmpBase = {};
|
|
47
|
+
let tmpBaseSchemaKeys = Object.keys(_RuntimeSyncProperties);
|
|
48
|
+
for (let i = 0; i < tmpBaseSchemaKeys.length; i++)
|
|
49
|
+
{
|
|
50
|
+
let tmpKey = tmpBaseSchemaKeys[i];
|
|
51
|
+
if (!pBody.hasOwnProperty(tmpKey)) continue;
|
|
52
|
+
let tmpCoerced = _RuntimeSyncProperties[tmpKey](pBody[tmpKey]);
|
|
53
|
+
if (tmpCoerced === null) continue;
|
|
54
|
+
tmpBase[tmpKey] = tmpCoerced;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let tmpPerEntity = (pBody.SyncEntityOptions && typeof(pBody.SyncEntityOptions) === 'object') ? pBody.SyncEntityOptions : {};
|
|
58
|
+
|
|
59
|
+
// Mirror base values onto the MeadowSync orchestrator + cloneState.
|
|
60
|
+
let tmpBaseKeys = Object.keys(tmpBase);
|
|
61
|
+
for (let i = 0; i < tmpBaseKeys.length; i++)
|
|
62
|
+
{
|
|
63
|
+
if (pMeadowSync.hasOwnProperty(tmpBaseKeys[i]))
|
|
64
|
+
{
|
|
65
|
+
pMeadowSync[tmpBaseKeys[i]] = tmpBase[tmpBaseKeys[i]];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (tmpBase.hasOwnProperty('SyncDeletedRecords'))
|
|
69
|
+
{
|
|
70
|
+
pCloneState.SyncDeletedRecords = tmpBase.SyncDeletedRecords;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Apply base to every entity.
|
|
74
|
+
let tmpAllEntityNames = Object.keys(pMeadowSync.MeadowSyncEntities || {});
|
|
75
|
+
for (let i = 0; i < tmpAllEntityNames.length; i++)
|
|
76
|
+
{
|
|
77
|
+
let tmpEntity = pMeadowSync.MeadowSyncEntities[tmpAllEntityNames[i]];
|
|
78
|
+
for (let j = 0; j < tmpBaseKeys.length; j++)
|
|
79
|
+
{
|
|
80
|
+
tmpEntity[tmpBaseKeys[j]] = tmpBase[tmpBaseKeys[j]];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Layer per-entity overrides on top. Log per entity that actually had
|
|
85
|
+
// overrides applied; warn on unknown entity names.
|
|
86
|
+
let tmpPerEntityNames = Object.keys(tmpPerEntity);
|
|
87
|
+
for (let i = 0; i < tmpPerEntityNames.length; i++)
|
|
88
|
+
{
|
|
89
|
+
let tmpEntityName = tmpPerEntityNames[i];
|
|
90
|
+
let tmpEntity = pMeadowSync.MeadowSyncEntities && pMeadowSync.MeadowSyncEntities[tmpEntityName];
|
|
91
|
+
if (!tmpEntity)
|
|
92
|
+
{
|
|
93
|
+
pLog.warn(`SyncEntityOptions: no sync entity ${tmpEntityName} loaded; ignoring overrides for it.`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
let tmpOverrides = tmpPerEntity[tmpEntityName];
|
|
97
|
+
if (!tmpOverrides || typeof(tmpOverrides) !== 'object') continue;
|
|
98
|
+
let tmpApplied = [];
|
|
99
|
+
let tmpOverrideKeys = Object.keys(tmpOverrides);
|
|
100
|
+
for (let j = 0; j < tmpOverrideKeys.length; j++)
|
|
101
|
+
{
|
|
102
|
+
let tmpKey = tmpOverrideKeys[j];
|
|
103
|
+
if (!_RuntimeSyncProperties.hasOwnProperty(tmpKey)) continue;
|
|
104
|
+
let tmpCoerced = _RuntimeSyncProperties[tmpKey](tmpOverrides[tmpKey]);
|
|
105
|
+
if (tmpCoerced === null) continue;
|
|
106
|
+
tmpEntity[tmpKey] = tmpCoerced;
|
|
107
|
+
tmpApplied.push(`${tmpKey}=${tmpCoerced}`);
|
|
108
|
+
}
|
|
109
|
+
if (tmpApplied.length > 0)
|
|
110
|
+
{
|
|
111
|
+
pLog.info(`SyncEntityOptions: ${tmpEntityName} ← ${tmpApplied.join(', ')}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
13
116
|
module.exports = (pDataClonerService, pOratorServiceServer) =>
|
|
14
117
|
{
|
|
15
118
|
let tmpFable = pDataClonerService.fable;
|
|
@@ -43,81 +146,9 @@ module.exports = (pDataClonerService, pOratorServiceServer) =>
|
|
|
43
146
|
let tmpRequestedMode = tmpBody.SyncMode || 'Initial';
|
|
44
147
|
let tmpMaxRecords = parseInt(tmpBody.MaxRecordsPerEntity, 10) || 0;
|
|
45
148
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
tmpCloneState.SyncDeletedRecords = !!tmpBody.SyncDeletedRecords;
|
|
50
|
-
let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
|
|
51
|
-
for (let i = 0; i < tmpEntityNames.length; i++)
|
|
52
|
-
{
|
|
53
|
-
tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].SyncDeletedRecords = tmpCloneState.SyncDeletedRecords;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Update MaxRecordsPerEntity on sync entities
|
|
58
|
-
if (tmpMaxRecords > 0)
|
|
59
|
-
{
|
|
60
|
-
let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
|
|
61
|
-
for (let i = 0; i < tmpEntityNames.length; i++)
|
|
62
|
-
{
|
|
63
|
-
tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].MaxRecordsPerEntity = tmpMaxRecords;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Update UseAdvancedIDPagination on all sync entities
|
|
68
|
-
if (tmpBody.hasOwnProperty('UseAdvancedIDPagination'))
|
|
69
|
-
{
|
|
70
|
-
let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
|
|
71
|
-
for (let i = 0; i < tmpEntityNames.length; i++)
|
|
72
|
-
{
|
|
73
|
-
tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].UseAdvancedIDPagination = !!tmpBody.UseAdvancedIDPagination;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Update DateTimePrecisionMS on MeadowSync and all sync entities
|
|
78
|
-
if (tmpBody.hasOwnProperty('DateTimePrecisionMS'))
|
|
79
|
-
{
|
|
80
|
-
let tmpPrecision = parseInt(tmpBody.DateTimePrecisionMS, 10);
|
|
81
|
-
if (!isNaN(tmpPrecision))
|
|
82
|
-
{
|
|
83
|
-
tmpFable.MeadowSync.DateTimePrecisionMS = tmpPrecision;
|
|
84
|
-
let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
|
|
85
|
-
for (let i = 0; i < tmpEntityNames.length; i++)
|
|
86
|
-
{
|
|
87
|
-
tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].DateTimePrecisionMS = tmpPrecision;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Update BackSyncTimeLimit on MeadowSync and all sync entities
|
|
93
|
-
if (tmpBody.hasOwnProperty('BackSyncTimeLimit'))
|
|
94
|
-
{
|
|
95
|
-
let tmpBackSyncTimeLimit = parseInt(tmpBody.BackSyncTimeLimit, 10);
|
|
96
|
-
if (!isNaN(tmpBackSyncTimeLimit) && tmpBackSyncTimeLimit > 0)
|
|
97
|
-
{
|
|
98
|
-
tmpFable.MeadowSync.BackSyncTimeLimit = tmpBackSyncTimeLimit;
|
|
99
|
-
let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
|
|
100
|
-
for (let i = 0; i < tmpEntityNames.length; i++)
|
|
101
|
-
{
|
|
102
|
-
tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].BackSyncTimeLimit = tmpBackSyncTimeLimit;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Update TrueUpPageSize on MeadowSync and all sync entities
|
|
108
|
-
if (tmpBody.hasOwnProperty('TrueUpPageSize'))
|
|
109
|
-
{
|
|
110
|
-
let tmpTrueUpPageSize = parseInt(tmpBody.TrueUpPageSize, 10);
|
|
111
|
-
if (!isNaN(tmpTrueUpPageSize) && tmpTrueUpPageSize > 0)
|
|
112
|
-
{
|
|
113
|
-
tmpFable.MeadowSync.TrueUpPageSize = tmpTrueUpPageSize;
|
|
114
|
-
let tmpEntityNames = Object.keys(tmpFable.MeadowSync.MeadowSyncEntities);
|
|
115
|
-
for (let i = 0; i < tmpEntityNames.length; i++)
|
|
116
|
-
{
|
|
117
|
-
tmpFable.MeadowSync.MeadowSyncEntities[tmpEntityNames[i]].TrueUpPageSize = tmpTrueUpPageSize;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
149
|
+
// Apply global + per-entity runtime sync overrides. See
|
|
150
|
+
// applyRuntimeSyncOverrides() at module scope.
|
|
151
|
+
applyRuntimeSyncOverrides(tmpBody, tmpFable.MeadowSync, tmpCloneState, tmpFable.log);
|
|
121
152
|
|
|
122
153
|
// If no tables specified, sync all entities
|
|
123
154
|
if (tmpSelectedTables.length === 0)
|
|
@@ -579,3 +610,7 @@ module.exports = (pDataClonerService, pOratorServiceServer) =>
|
|
|
579
610
|
return fNext();
|
|
580
611
|
});
|
|
581
612
|
};
|
|
613
|
+
|
|
614
|
+
// Exposed for unit tests.
|
|
615
|
+
module.exports._RuntimeSyncProperties = _RuntimeSyncProperties;
|
|
616
|
+
module.exports.applyRuntimeSyncOverrides = applyRuntimeSyncOverrides;
|
|
@@ -2,35 +2,48 @@ const libPictApplication = require('pict-application');
|
|
|
2
2
|
|
|
3
3
|
const libProvider = require('./providers/Pict-Provider-DataCloner.js');
|
|
4
4
|
|
|
5
|
-
const libViewLayout
|
|
5
|
+
const libViewLayout = require('./views/PictView-DataCloner-Layout.js');
|
|
6
6
|
const libViewConnection = require('./views/PictView-DataCloner-Connection.js');
|
|
7
|
-
const libViewSession
|
|
8
|
-
const libViewSchema
|
|
9
|
-
const libViewDeploy
|
|
10
|
-
const libViewSync
|
|
11
|
-
const libViewExport
|
|
12
|
-
const libViewViewData
|
|
13
|
-
const libViewHistogram
|
|
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
14
|
const libViewConnectionForm = require('pict-section-connection-form');
|
|
15
15
|
|
|
16
|
+
const libPictSectionModal = require('pict-section-modal');
|
|
17
|
+
const libPictSectionTheme = require('pict-section-theme');
|
|
18
|
+
const libBrand = require('../../RetoldDataService-Brand.js');
|
|
19
|
+
|
|
20
|
+
const libViewShell = require('./views/PictView-DataCloner-Shell.js');
|
|
21
|
+
const libViewTopBarNav = require('./views/PictView-DataCloner-TopBar-Nav.js');
|
|
22
|
+
const libViewTopBarUser = require('./views/PictView-DataCloner-TopBar-User.js');
|
|
23
|
+
const libViewStatusBar = require('./views/PictView-DataCloner-StatusBar.js');
|
|
24
|
+
const libViewStatusDetail = require('./views/PictView-DataCloner-StatusDetail.js');
|
|
25
|
+
const libViewSettings = require('./views/PictView-DataCloner-SettingsPanel.js');
|
|
26
|
+
|
|
16
27
|
class DataClonerApplication extends libPictApplication
|
|
17
28
|
{
|
|
18
29
|
constructor(pFable, pOptions, pServiceHash)
|
|
19
30
|
{
|
|
20
31
|
super(pFable, pOptions, pServiceHash);
|
|
21
32
|
|
|
22
|
-
//
|
|
23
|
-
this.pict.
|
|
33
|
+
// 1. Modal section (provides shell + panels + modal API).
|
|
34
|
+
this.pict.addView('Pict-Section-Modal',
|
|
35
|
+
libPictSectionModal.default_configuration, libPictSectionModal);
|
|
24
36
|
|
|
25
|
-
//
|
|
26
|
-
this.pict.
|
|
37
|
+
// 2. Provider + existing section views.
|
|
38
|
+
this.pict.addProvider('DataCloner', libProvider.default_configuration, libProvider);
|
|
39
|
+
this.pict.addView('DataCloner-Layout', libViewLayout.default_configuration, libViewLayout);
|
|
27
40
|
this.pict.addView('DataCloner-Connection', libViewConnection.default_configuration, libViewConnection);
|
|
28
|
-
this.pict.addView('DataCloner-Session',
|
|
29
|
-
this.pict.addView('DataCloner-Schema',
|
|
30
|
-
this.pict.addView('DataCloner-Deploy',
|
|
31
|
-
this.pict.addView('DataCloner-Sync',
|
|
32
|
-
this.pict.addView('DataCloner-Export',
|
|
33
|
-
this.pict.addView('DataCloner-ViewData',
|
|
41
|
+
this.pict.addView('DataCloner-Session', libViewSession.default_configuration, libViewSession);
|
|
42
|
+
this.pict.addView('DataCloner-Schema', libViewSchema.default_configuration, libViewSchema);
|
|
43
|
+
this.pict.addView('DataCloner-Deploy', libViewDeploy.default_configuration, libViewDeploy);
|
|
44
|
+
this.pict.addView('DataCloner-Sync', libViewSync.default_configuration, libViewSync);
|
|
45
|
+
this.pict.addView('DataCloner-Export', libViewExport.default_configuration, libViewExport);
|
|
46
|
+
this.pict.addView('DataCloner-ViewData', libViewViewData.default_configuration, libViewViewData);
|
|
34
47
|
this.pict.addView('DataCloner-StatusHistogram',
|
|
35
48
|
{
|
|
36
49
|
ViewIdentifier: 'DataCloner-StatusHistogram',
|
|
@@ -43,14 +56,12 @@ class DataClonerApplication extends libPictApplication
|
|
|
43
56
|
ShowValues: false,
|
|
44
57
|
ShowLabels: true,
|
|
45
58
|
MaxBarSize: 80,
|
|
46
|
-
BarColor: '#4a90d9',
|
|
59
|
+
BarColor: 'var(--theme-color-brand-primary, #4a90d9)',
|
|
47
60
|
Bins: []
|
|
48
61
|
}, libViewHistogram);
|
|
49
62
|
|
|
50
|
-
// Shared schema-driven connection form
|
|
51
|
-
//
|
|
52
|
-
// provider's bootstrapConnectionSchemas() pumps the schemas in
|
|
53
|
-
// once the host's /clone/connection/schemas endpoint responds.
|
|
63
|
+
// Shared schema-driven connection form (renders into the
|
|
64
|
+
// DataCloner-Connection accordion shell's slot).
|
|
54
65
|
this.pict.addView('PictSection-ConnectionForm',
|
|
55
66
|
Object.assign({}, libViewConnectionForm.default_configuration,
|
|
56
67
|
{
|
|
@@ -60,21 +71,39 @@ class DataClonerApplication extends libPictApplication
|
|
|
60
71
|
ActiveAddress: 'AppData.DataCloner.Connection.ActiveProvider',
|
|
61
72
|
FieldIDPrefix: 'datacloner-conn'
|
|
62
73
|
}), libViewConnectionForm);
|
|
74
|
+
|
|
75
|
+
// 3. Shell host + slot views + status bar / detail + settings panel.
|
|
76
|
+
this.pict.addView('DataCloner-Shell',
|
|
77
|
+
libViewShell.default_configuration, libViewShell);
|
|
78
|
+
this.pict.addView('DataCloner-TopBar-Nav',
|
|
79
|
+
libViewTopBarNav.default_configuration, libViewTopBarNav);
|
|
80
|
+
this.pict.addView('DataCloner-TopBar-User',
|
|
81
|
+
libViewTopBarUser.default_configuration, libViewTopBarUser);
|
|
82
|
+
this.pict.addView('DataCloner-StatusBar',
|
|
83
|
+
libViewStatusBar.default_configuration, libViewStatusBar);
|
|
84
|
+
this.pict.addView('DataCloner-StatusDetail',
|
|
85
|
+
libViewStatusDetail.default_configuration, libViewStatusDetail);
|
|
86
|
+
this.pict.addView('DataCloner-SettingsPanel',
|
|
87
|
+
libViewSettings.default_configuration, libViewSettings);
|
|
88
|
+
|
|
89
|
+
// 4. Theme-Section provider — registered LAST so it can find the slot views.
|
|
90
|
+
this.pict.addProvider('Theme-Section',
|
|
91
|
+
{
|
|
92
|
+
ApplyDefault: 'pict-default',
|
|
93
|
+
DefaultMode: 'system',
|
|
94
|
+
DefaultScale: 1.0,
|
|
95
|
+
Brand: libBrand,
|
|
96
|
+
Views: ['Picker', 'ModeToggle', 'ScaleSelect', 'BrandMark', 'TopBar', 'BottomBar'],
|
|
97
|
+
ViewOptions:
|
|
98
|
+
{
|
|
99
|
+
TopBar: { NavView: 'DataCloner-TopBar-Nav', UserView: 'DataCloner-TopBar-User', Height: 56 },
|
|
100
|
+
BottomBar: { StatusView: 'DataCloner-StatusBar', Height: 36 }
|
|
101
|
+
}
|
|
102
|
+
}, libPictSectionTheme);
|
|
63
103
|
}
|
|
64
104
|
|
|
65
105
|
onAfterInitializeAsync(fCallback)
|
|
66
106
|
{
|
|
67
|
-
// Centralized state (replaces global variables).
|
|
68
|
-
//
|
|
69
|
-
// PersistFields covers the static, non-connection inputs only.
|
|
70
|
-
// Connection-section fields (provider picker + per-provider
|
|
71
|
-
// inputs) are schema-driven now: their DOM ids and
|
|
72
|
-
// localStorage keys are derived at runtime from the host's
|
|
73
|
-
// /clone/connection/schemas response and persistence is hooked
|
|
74
|
-
// up by Pict-Provider-DataCloner#bootstrapConnectionSchemas
|
|
75
|
-
// after the schema-driven Connection view re-renders. See
|
|
76
|
-
// PictView-DataCloner-Connection.js for the field-id
|
|
77
|
-
// convention.
|
|
78
107
|
this.pict.AppData.DataCloner =
|
|
79
108
|
{
|
|
80
109
|
FetchedTables: [],
|
|
@@ -94,9 +123,6 @@ class DataClonerApplication extends libPictApplication
|
|
|
94
123
|
'userName', 'password', 'schemaURL', 'pageSize', 'dateTimePrecisionMS',
|
|
95
124
|
'syncMaxRecords'
|
|
96
125
|
],
|
|
97
|
-
// Connection state — populated by bootstrapConnectionSchemas().
|
|
98
|
-
// Initialized empty here so the Connection view's first
|
|
99
|
-
// onBeforeRender finds a valid (if empty) shape.
|
|
100
126
|
Connection:
|
|
101
127
|
{
|
|
102
128
|
Schemas: [],
|
|
@@ -108,15 +134,17 @@ class DataClonerApplication extends libPictApplication
|
|
|
108
134
|
}
|
|
109
135
|
};
|
|
110
136
|
|
|
111
|
-
// Make pict available for inline onclick handlers
|
|
112
137
|
window.pict = this.pict;
|
|
113
138
|
|
|
114
|
-
// Render
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
// Render the shell first — creates panel destination divs.
|
|
140
|
+
this.pict.views['DataCloner-Shell'].render();
|
|
141
|
+
|
|
142
|
+
// Render the layout (chains child renders) into #DataCloner-Workspace.
|
|
117
143
|
this.pict.views['DataCloner-Layout'].render();
|
|
118
144
|
|
|
119
|
-
//
|
|
145
|
+
// Render the StatusBar into the BottomBar slot.
|
|
146
|
+
this.pict.views['DataCloner-StatusBar'].render();
|
|
147
|
+
|
|
120
148
|
this.pict.providers.DataCloner.initPersistence();
|
|
121
149
|
this.pict.providers.DataCloner.restoreDeployedTables();
|
|
122
150
|
this.pict.providers.DataCloner.startLiveStatusPolling();
|
|
@@ -125,11 +153,6 @@ class DataClonerApplication extends libPictApplication
|
|
|
125
153
|
this.pict.views['DataCloner-Layout'].collapseAllSections();
|
|
126
154
|
this.pict.providers.DataCloner.initAutoProcess();
|
|
127
155
|
|
|
128
|
-
// Async: fetch the host's connection-form schemas and re-render
|
|
129
|
-
// the Connection section. bootstrapConnectionSchemas restores
|
|
130
|
-
// localStorage values + hooks save listeners once the new DOM
|
|
131
|
-
// is in place, then invokes onProviderChange() to surface the
|
|
132
|
-
// active provider's form.
|
|
133
156
|
this.pict.providers.DataCloner.bootstrapConnectionSchemas(function () { /* fire-and-forget */ });
|
|
134
157
|
|
|
135
158
|
return fCallback();
|
|
@@ -733,7 +733,7 @@ class DataClonerProvider extends libPictProvider
|
|
|
733
733
|
tmpHtml += '<tr' + tmpRowClass + '>';
|
|
734
734
|
tmpHtml += '<td>' + this.escapeHtml(tmpT.Name) + '</td>';
|
|
735
735
|
tmpHtml += '<td style="text-align:right; font-variant-numeric:tabular-nums">' + tmpCountFmt + '</td>';
|
|
736
|
-
tmpHtml += '<td style="text-align:right; font-variant-numeric:tabular-nums; color
|
|
736
|
+
tmpHtml += '<td style="text-align:right; font-variant-numeric:tabular-nums; color:var(--theme-color-text-muted, #888)">' + tmpTimeFmt + '</td>';
|
|
737
737
|
tmpHtml += '</tr>';
|
|
738
738
|
}
|
|
739
739
|
tmpHtml += '</tbody>';
|
|
@@ -908,11 +908,11 @@ class DataClonerProvider extends libPictProvider
|
|
|
908
908
|
{
|
|
909
909
|
if (tmpIsLive)
|
|
910
910
|
{
|
|
911
|
-
tmpHtml = '<div style="font-size:0.9em; color
|
|
911
|
+
tmpHtml = '<div style="font-size:0.9em; color:var(--theme-color-text-muted, #888); padding:8px 0">Sync in progress, waiting for table data\u2026</div>';
|
|
912
912
|
}
|
|
913
913
|
else
|
|
914
914
|
{
|
|
915
|
-
tmpHtml = '<div style="font-size:0.9em; color
|
|
915
|
+
tmpHtml = '<div style="font-size:0.9em; color:var(--theme-color-text-muted, #888); padding:8px 0">No sync data available. Run a sync to see operation details here.</div>';
|
|
916
916
|
}
|
|
917
917
|
}
|
|
918
918
|
|
|
@@ -51,7 +51,7 @@ const _ViewConfiguration =
|
|
|
51
51
|
<div class="accordion-toggle">▼</div>
|
|
52
52
|
</div>
|
|
53
53
|
<div class="accordion-body">
|
|
54
|
-
<p style="font-size:0.9em; color
|
|
54
|
+
<p style="font-size:0.9em; color:var(--theme-color-text-secondary, #666); margin-bottom:10px">Configure the local database where cloned data will be stored. The provider list comes from the host's installed meadow-connection modules.</p>
|
|
55
55
|
|
|
56
56
|
<div class="inline-group" style="margin-bottom:10px">
|
|
57
57
|
<div style="flex:1; display:flex; align-items:flex-end; gap:8px; justify-content:flex-end">
|
|
@@ -62,7 +62,7 @@ class DataClonerDeployView extends libPictView
|
|
|
62
62
|
auditGUIDIndices()
|
|
63
63
|
{
|
|
64
64
|
let tmpReportEl = document.getElementById('guidIndexReport');
|
|
65
|
-
if (tmpReportEl) tmpReportEl.innerHTML = '<span style="color
|
|
65
|
+
if (tmpReportEl) tmpReportEl.innerHTML = '<span style="color:var(--theme-color-text-muted, #888)">Checking GUID indices...</span>';
|
|
66
66
|
|
|
67
67
|
let tmpSelf = this;
|
|
68
68
|
this.pict.providers.DataCloner.api('GET', '/clone/schema/guid-index-audit')
|
|
@@ -84,7 +84,7 @@ class DataClonerDeployView extends libPictView
|
|
|
84
84
|
|
|
85
85
|
let tmpHTML = '<div style="margin-top:6px"><strong>' + pData.Message + '</strong></div>';
|
|
86
86
|
tmpHTML += '<table style="font-size:0.85em; margin:6px 0; border-collapse:collapse; width:100%">';
|
|
87
|
-
tmpHTML += '<tr style="text-align:left; border-bottom:1px solid #ccc"><th style="padding:3px 8px">Table</th><th style="padding:3px 8px">GUID Column</th><th style="padding:3px 8px">Index</th></tr>';
|
|
87
|
+
tmpHTML += '<tr style="text-align:left; border-bottom:1px solid var(--theme-color-border-default, #ccc)"><th style="padding:3px 8px">Table</th><th style="padding:3px 8px">GUID Column</th><th style="padding:3px 8px">Index</th></tr>';
|
|
88
88
|
|
|
89
89
|
for (let t = 0; t < pData.Tables.length; t++)
|
|
90
90
|
{
|
|
@@ -95,7 +95,7 @@ class DataClonerDeployView extends libPictView
|
|
|
95
95
|
let tmpStatus = tmpCol.HasIndex
|
|
96
96
|
? '<span style="color:green">' + tmpCol.IndexName + '</span>'
|
|
97
97
|
: '<span style="color:red">MISSING</span>';
|
|
98
|
-
tmpHTML += '<tr style="border-bottom:1px solid #eee"><td style="padding:3px 8px">' + tmpTable.Table + '</td><td style="padding:3px 8px">' + tmpCol.Column + '</td><td style="padding:3px 8px">' + tmpStatus + '</td></tr>';
|
|
98
|
+
tmpHTML += '<tr style="border-bottom:1px solid var(--theme-color-border-light, #eee)"><td style="padding:3px 8px">' + tmpTable.Table + '</td><td style="padding:3px 8px">' + tmpCol.Column + '</td><td style="padding:3px 8px">' + tmpStatus + '</td></tr>';
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
tmpHTML += '</table>';
|
|
@@ -112,7 +112,7 @@ class DataClonerDeployView extends libPictView
|
|
|
112
112
|
createMissingGUIDIndices()
|
|
113
113
|
{
|
|
114
114
|
let tmpReportEl = document.getElementById('guidIndexReport');
|
|
115
|
-
if (tmpReportEl) tmpReportEl.innerHTML = '<span style="color
|
|
115
|
+
if (tmpReportEl) tmpReportEl.innerHTML = '<span style="color:var(--theme-color-text-muted, #888)">Creating GUID indices...</span>';
|
|
116
116
|
|
|
117
117
|
let tmpSelf = this;
|
|
118
118
|
this.pict.providers.DataCloner.api('POST', '/clone/schema/guid-index-create')
|
|
@@ -206,7 +206,7 @@ module.exports.default_configuration =
|
|
|
206
206
|
<div class="accordion-toggle">▼</div>
|
|
207
207
|
</div>
|
|
208
208
|
<div class="accordion-body">
|
|
209
|
-
<p style="font-size:0.9em; color
|
|
209
|
+
<p style="font-size:0.9em; color:var(--theme-color-text-secondary, #666); margin-bottom:10px">Creates the selected tables in the local database and sets up CRUD endpoints (e.g. GET /1.0/Documents).</p>
|
|
210
210
|
<button class="primary" onclick="pict.views['DataCloner-Deploy'].deploySchema()">Deploy Selected Tables</button>
|
|
211
211
|
<button onclick="pict.views['DataCloner-Deploy'].auditGUIDIndices()">Check GUID Indices</button>
|
|
212
212
|
<button class="danger" onclick="pict.views['DataCloner-Deploy'].resetDatabase()">Reset Database</button>
|
|
@@ -450,7 +450,7 @@ module.exports.default_configuration =
|
|
|
450
450
|
<div class="accordion-toggle">▼</div>
|
|
451
451
|
</div>
|
|
452
452
|
<div class="accordion-body">
|
|
453
|
-
<p style="font-size:0.9em; color
|
|
453
|
+
<p style="font-size:0.9em; color:var(--theme-color-text-secondary, #666); margin-bottom:10px">Generate a JSON config file from your current settings. Use it to run headless clones from the command line.</p>
|
|
454
454
|
<div style="display:flex; gap:8px; margin-bottom:10px">
|
|
455
455
|
<button class="primary" onclick="pict.views['DataCloner-Export'].generateConfig()">Generate Config</button>
|
|
456
456
|
<button class="secondary" onclick="pict.views['DataCloner-Export'].copyConfig()">Copy to Clipboard</button>
|
|
@@ -458,28 +458,28 @@ module.exports.default_configuration =
|
|
|
458
458
|
</div>
|
|
459
459
|
<div id="configExportStatus"></div>
|
|
460
460
|
<div id="cliCommand" style="display:none; margin-bottom:10px">
|
|
461
|
-
<label style="margin-bottom:4px">CLI Command <span style="color
|
|
462
|
-
<div style="background
|
|
461
|
+
<label style="margin-bottom:4px">CLI Command <span style="color:var(--theme-color-text-muted, #888); font-weight:normal">(with config file)</span></label>
|
|
462
|
+
<div style="background:var(--theme-color-text-primary, #1a1a1a); color:var(--theme-color-status-info, #4fc3f7); padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer" onclick="pict.views['DataCloner-Export'].copyCLI()" title="Click to copy"></div>
|
|
463
463
|
</div>
|
|
464
464
|
<div id="cliOneShot" style="display:none; margin-bottom:10px">
|
|
465
|
-
<label style="margin-bottom:4px">One-liner <span style="color
|
|
466
|
-
<div style="background
|
|
465
|
+
<label style="margin-bottom:4px">One-liner <span style="color:var(--theme-color-text-muted, #888); font-weight:normal">(no config file needed)</span></label>
|
|
466
|
+
<div style="background:var(--theme-color-text-primary, #1a1a1a); color:var(--theme-color-status-info, #4fc3f7); padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer; white-space:pre-wrap" onclick="pict.views['DataCloner-Export'].copyOneShot()" title="Click to copy"></div>
|
|
467
467
|
</div>
|
|
468
|
-
<textarea id="configOutput" style="display:none; width:100%; min-height:300px; font-family:monospace; font-size:0.85em; padding:10px; border:1px solid #ccc; border-radius:4px; background
|
|
468
|
+
<textarea id="configOutput" style="display:none; width:100%; min-height:300px; font-family:monospace; font-size:0.85em; padding:10px; border:1px solid var(--theme-color-border-default, #ccc); border-radius:4px; background:var(--theme-color-background-secondary, #fafafa); tab-size:4; resize:vertical" readonly></textarea>
|
|
469
469
|
|
|
470
|
-
<div id="mdwintExport" style="display:none; margin-top:16px; padding-top:16px; border-top:1px solid #eee">
|
|
471
|
-
<h3 style="margin:0 0 8px; font-size:1em">meadow-integration CLI <span style="color
|
|
472
|
-
<p style="font-size:0.85em; color
|
|
470
|
+
<div id="mdwintExport" style="display:none; margin-top:16px; padding-top:16px; border-top:1px solid var(--theme-color-border-light, #eee)">
|
|
471
|
+
<h3 style="margin:0 0 8px; font-size:1em">meadow-integration CLI <span style="color:var(--theme-color-text-muted, #888); font-weight:normal; font-size:0.85em">(mdwint clone)</span></h3>
|
|
472
|
+
<p style="font-size:0.85em; color:var(--theme-color-text-secondary, #666); margin-bottom:8px">Save as <code>.meadow.config.json</code> in your project root, then run the command below. Requires a local Meadow extended schema JSON file.</p>
|
|
473
473
|
<div style="display:flex; gap:8px; margin-bottom:10px">
|
|
474
474
|
<button class="secondary" onclick="pict.views['DataCloner-Export'].copyMdwintConfig()">Copy Config</button>
|
|
475
475
|
<button class="secondary" onclick="pict.views['DataCloner-Export'].downloadMdwintConfig()">Download .meadow.config.json</button>
|
|
476
476
|
</div>
|
|
477
477
|
<div id="mdwintCLICommand" style="margin-bottom:10px">
|
|
478
478
|
<label style="margin-bottom:4px">CLI Command</label>
|
|
479
|
-
<div style="background
|
|
479
|
+
<div style="background:var(--theme-color-text-primary, #1a1a1a); color:var(--theme-color-status-info, #4fc3f7); padding:10px 14px; border-radius:4px; font-family:monospace; font-size:0.9em; word-break:break-all; cursor:pointer" onclick="pict.views['DataCloner-Export'].copyMdwintCLI()" title="Click to copy"></div>
|
|
480
480
|
</div>
|
|
481
481
|
<div id="mdwintConfigStatus"></div>
|
|
482
|
-
<textarea id="mdwintConfigOutput" style="width:100%; min-height:250px; font-family:monospace; font-size:0.85em; padding:10px; border:1px solid #ccc; border-radius:4px; background
|
|
482
|
+
<textarea id="mdwintConfigOutput" style="width:100%; min-height:250px; font-family:monospace; font-size:0.85em; padding:10px; border:1px solid var(--theme-color-border-default, #ccc); border-radius:4px; background:var(--theme-color-background-secondary, #fafafa); tab-size:4; resize:vertical" readonly></textarea>
|
|
483
483
|
</div>
|
|
484
484
|
</div>
|
|
485
485
|
</div>
|