@static-suite/static-suite-data-server 1.0.4 → 1.0.6
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/dist/http/routes/diff.d.ts +7 -0
- package/dist/http/routes/diff.d.ts.map +1 -0
- package/dist/http/routes/diff.js +40 -0
- package/dist/http/routes/home.d.ts.map +1 -1
- package/dist/http/routes/home.js +1 -0
- package/dist/http/routes/index.d.ts.map +1 -1
- package/dist/http/routes/index.js +5 -0
- package/dist/http/views/diffIndex.pug +14 -0
- package/dist/lib/dataServer.d.ts.map +1 -1
- package/dist/lib/dataServer.js +11 -0
- package/dist/lib/store/diff/diff.types.d.ts.map +1 -1
- package/dist/lib/store/diff/diffManager.d.ts.map +1 -1
- package/dist/lib/store/dump/dump.types.d.ts +4 -0
- package/dist/lib/store/dump/dump.types.d.ts.map +1 -1
- package/dist/lib/store/dump/dumpIndexHelper.d.ts +14 -0
- package/dist/lib/store/dump/dumpIndexHelper.d.ts.map +1 -0
- package/dist/lib/store/dump/dumpIndexHelper.js +112 -0
- package/dist/lib/store/dump/dumpManager.d.ts.map +1 -1
- package/dist/lib/store/dump/dumpManager.js +81 -48
- package/dist/lib/store/dump/dumpMetadataHelper.d.ts.map +1 -1
- package/dist/lib/store/dump/dumpMetadataHelper.js +1 -0
- package/dist/lib/utils/string/stringUtils.d.ts +6 -0
- package/dist/lib/utils/string/stringUtils.d.ts.map +1 -1
- package/dist/lib/utils/string/stringUtils.js +8 -1
- package/package.json +3 -2
- package/src/http/routes/diff.ts +47 -0
- package/src/http/routes/home.ts +1 -0
- package/src/http/routes/index.ts +5 -0
- package/src/http/routes/subset.ts +1 -1
- package/src/http/views/diffIndex.pug +14 -0
- package/src/lib/dataServer.ts +17 -1
- package/src/lib/store/diff/diff.types.ts +1 -0
- package/src/lib/store/diff/diffManager.ts +1 -0
- package/src/lib/store/dump/dump.types.ts +5 -0
- package/src/lib/store/dump/dumpIndexHelper.ts +134 -0
- package/src/lib/store/dump/dumpManager.ts +112 -58
- package/src/lib/store/dump/dumpMetadataHelper.ts +1 -0
- package/src/lib/utils/string/stringUtils.ts +8 -0
- package/tsconfig.json +2 -2
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
declare const diffIndex: (req: Request, res: Response) => void;
|
|
3
|
+
declare const diffIncremental: (req: Request, res: Response) => void;
|
|
4
|
+
declare const diffFull: (req: Request, res: Response) => void;
|
|
5
|
+
declare const diffReset: (req: Request, res: Response) => void;
|
|
6
|
+
export { diffIndex, diffIncremental, diffFull, diffReset };
|
|
7
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/http/routes/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI5C,QAAA,MAAM,SAAS,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KAAG,IAShD,CAAC;AAeF,QAAA,MAAM,eAAe,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KAAG,IAEtD,CAAC;AAEF,QAAA,MAAM,QAAQ,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KAAG,IAE/C,CAAC;AAEF,QAAA,MAAM,SAAS,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KAAG,IAQhD,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.diffReset = exports.diffFull = exports.diffIncremental = exports.diffIndex = void 0;
|
|
4
|
+
const diff_1 = require("../../lib/store/diff");
|
|
5
|
+
const object_1 = require("../../lib/utils/object");
|
|
6
|
+
const diffIndex = (req, res) => {
|
|
7
|
+
res.render('diffIndex', {
|
|
8
|
+
links: {
|
|
9
|
+
'/diff/incremental': 'Execute an incremental dump',
|
|
10
|
+
'/diff/full': 'Execute a full dump',
|
|
11
|
+
'/diff/reset': 'Clears intermediate changes tracked by dependency manager',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
exports.diffIndex = diffIndex;
|
|
16
|
+
const diffAction = (req, res, incremental) => {
|
|
17
|
+
const diff = diff_1.diffManager.getDiff({ incremental });
|
|
18
|
+
const diffAsJson = (0, object_1.jsonify)(diff);
|
|
19
|
+
res.status(200);
|
|
20
|
+
res.set({ 'Content-Type': 'application/json' });
|
|
21
|
+
res.send(diffAsJson);
|
|
22
|
+
};
|
|
23
|
+
const diffIncremental = (req, res) => {
|
|
24
|
+
diffAction(req, res, true);
|
|
25
|
+
};
|
|
26
|
+
exports.diffIncremental = diffIncremental;
|
|
27
|
+
const diffFull = (req, res) => {
|
|
28
|
+
diffAction(req, res, false);
|
|
29
|
+
};
|
|
30
|
+
exports.diffFull = diffFull;
|
|
31
|
+
const diffReset = (req, res) => {
|
|
32
|
+
const args = req.query;
|
|
33
|
+
if (args?.uniqueId) {
|
|
34
|
+
diff_1.diffManager.reset(args.uniqueId);
|
|
35
|
+
}
|
|
36
|
+
res.status(200);
|
|
37
|
+
res.set({ 'Content-Type': 'application/json' });
|
|
38
|
+
res.send({ status: 'done' });
|
|
39
|
+
};
|
|
40
|
+
exports.diffReset = diffReset;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"home.d.ts","sourceRoot":"","sources":["../../../src/http/routes/home.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,IAAI,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KAAG,
|
|
1
|
+
{"version":3,"file":"home.d.ts","sourceRoot":"","sources":["../../../src/http/routes/home.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,IAAI,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KAAG,IAclD,CAAC"}
|
package/dist/http/routes/home.js
CHANGED
|
@@ -6,6 +6,7 @@ const home = (req, res) => {
|
|
|
6
6
|
links: {
|
|
7
7
|
'/data': 'Browse all data loaded into the server',
|
|
8
8
|
'/dependency': 'Dependency data',
|
|
9
|
+
'/diff': 'Diff operations',
|
|
9
10
|
'/dump': 'Dump operations',
|
|
10
11
|
'/query': 'List of available queries',
|
|
11
12
|
'/task': 'List of available tasks',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/routes/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/http/routes/index.ts"],"names":[],"mappings":"AAoCA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAqCxB,eAAe,MAAM,CAAC"}
|
|
@@ -13,6 +13,7 @@ const dependency_1 = require("./dependency");
|
|
|
13
13
|
const indexUrl_1 = require("./indexUrl");
|
|
14
14
|
const indexUUID_1 = require("./indexUUID");
|
|
15
15
|
const subset_1 = require("./subset");
|
|
16
|
+
const diff_1 = require("./diff");
|
|
16
17
|
const routes = (0, express_1.Router)();
|
|
17
18
|
routes.get('/', home_1.home);
|
|
18
19
|
routes.get('/docs', docs_1.docs);
|
|
@@ -28,6 +29,10 @@ routes.get('/task', task_1.taskIndex);
|
|
|
28
29
|
routes.get('/task/:taskId', task_1.runTask);
|
|
29
30
|
routes.get('/cache', cache_1.cacheIndex);
|
|
30
31
|
routes.get('/cache/:cacheBinId/clear', cache_1.cacheClear);
|
|
32
|
+
routes.get('/diff', diff_1.diffIndex);
|
|
33
|
+
routes.get('/diff/incremental', diff_1.diffIncremental);
|
|
34
|
+
routes.get('/diff/full', diff_1.diffFull);
|
|
35
|
+
routes.get('/diff/reset', diff_1.diffReset);
|
|
31
36
|
routes.get('/dump', dump_1.dumpIndex);
|
|
32
37
|
routes.get('/dump/incremental', dump_1.dumpIncremental);
|
|
33
38
|
routes.get('/dump/full', dump_1.dumpFull);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
extends includes/layout.pug
|
|
2
|
+
|
|
3
|
+
block vars
|
|
4
|
+
- var title = "Diff | Static Suite Data Server"
|
|
5
|
+
|
|
6
|
+
block content
|
|
7
|
+
h1(class="title is-1")="Diff"
|
|
8
|
+
table(class="table is-fullwidth is-hoverable")
|
|
9
|
+
tbody
|
|
10
|
+
each description, link in links
|
|
11
|
+
tr
|
|
12
|
+
td
|
|
13
|
+
a(href=link)= link
|
|
14
|
+
td=description
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataServer.d.ts","sourceRoot":"","sources":["../../src/lib/dataServer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAKhE,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,WAAW,EACX,uBAAuB,EACvB,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"dataServer.d.ts","sourceRoot":"","sources":["../../src/lib/dataServer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAKhE,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,WAAW,EACX,uBAAuB,EACvB,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAG5B;;;;GAIG;AACH,QAAA,MAAM,UAAU;IACd;;;;;;OAMG;oBACa,qBAAqB,KAAG,gBAAgB;CA4CzD,CAAC;AACF,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,OAAO,EACP,KAAK,EACL,WAAW,EACX,uBAAuB,EACvB,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,UAAU,EACV,WAAW,GACZ,CAAC"}
|
package/dist/lib/dataServer.js
CHANGED
|
@@ -14,6 +14,7 @@ const query_types_1 = require("./query/query.types");
|
|
|
14
14
|
Object.defineProperty(exports, "CacheStatus", { enumerable: true, get: function () { return query_types_1.CacheStatus; } });
|
|
15
15
|
const dataServer_types_1 = require("./dataServer.types");
|
|
16
16
|
Object.defineProperty(exports, "RunMode", { enumerable: true, get: function () { return dataServer_types_1.RunMode; } });
|
|
17
|
+
const dumpIndexHelper_1 = require("./store/dump/dumpIndexHelper");
|
|
17
18
|
/**
|
|
18
19
|
* The Data Server instance.
|
|
19
20
|
*
|
|
@@ -28,6 +29,7 @@ const dataServer = {
|
|
|
28
29
|
* @returns An object with the data store and the queryRunner service.
|
|
29
30
|
*/
|
|
30
31
|
init: (options) => {
|
|
32
|
+
const startDate = Date.now();
|
|
31
33
|
// Configure logger.
|
|
32
34
|
(0, logger_1.configureLogger)(options.logLevel, options.logFile);
|
|
33
35
|
// Configure data server.
|
|
@@ -46,6 +48,15 @@ const dataServer = {
|
|
|
46
48
|
}
|
|
47
49
|
// Load data from dataDir.
|
|
48
50
|
dataDir_1.dataDirManager.load();
|
|
51
|
+
// Create and load dump index.
|
|
52
|
+
if (config.dumpDir) {
|
|
53
|
+
if (dumpIndexHelper_1.dumpIndexHelper.isDumpIndexStale() ||
|
|
54
|
+
!dumpIndexHelper_1.dumpIndexHelper.isDumpIndexPresent()) {
|
|
55
|
+
dumpIndexHelper_1.dumpIndexHelper.createDumpIndex();
|
|
56
|
+
}
|
|
57
|
+
dumpIndexHelper_1.dumpIndexHelper.loadDumpIndex();
|
|
58
|
+
}
|
|
59
|
+
logger_1.logger.info(`Data Server loaded in ${Date.now() - startDate}ms.`);
|
|
49
60
|
return {
|
|
50
61
|
store: store_1.store,
|
|
51
62
|
queryRunner: query_1.queryRunner,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.types.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/diff/diff.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACnD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG;IACjB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"diff.types.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/diff/diff.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACnD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG;IACjB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAErB;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACtB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diffManager.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/diff/diffManager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAQ,MAAM,cAAc,CAAC;AAQjD,eAAO,MAAM,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"diffManager.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/diff/diffManager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAQ,MAAM,cAAc,CAAC;AAQjD,eAAO,MAAM,WAAW,EAAE,WAwEzB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dump.types.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/dump/dump.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAE/C;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG;IACjB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,OAAO,EAAE,GAAG,CACV,MAAM,EACN;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAC7D,CAAC;IAEF;;;;;OAKG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAE1E;;OAEG;IACH,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,IAAI,EAAE,CAAC;CACf,CAAC"}
|
|
1
|
+
{"version":3,"file":"dump.types.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/dump/dump.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAE/C;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG;IACjB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,OAAO,EAAE,GAAG,CACV,MAAM,EACN;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAC7D,CAAC;IAEF;;;;;OAKG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAE1E;;OAEG;IACH,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,IAAI,EAAE,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IndexEntry } from './dump.types';
|
|
2
|
+
export declare const dumpIndexHelper: {
|
|
3
|
+
isDumpIndexStale: () => boolean;
|
|
4
|
+
isDumpIndexPresent: () => boolean;
|
|
5
|
+
saveDumpIndex: () => void;
|
|
6
|
+
createDumpIndex: () => void;
|
|
7
|
+
loadDumpIndex: () => void;
|
|
8
|
+
hasEntry: (relativeFilePath: string) => boolean;
|
|
9
|
+
getEntry: (relativeFilePath: string) => IndexEntry | undefined;
|
|
10
|
+
getKeys: () => MapIterator<string>;
|
|
11
|
+
addEntry: (relativeFilePath: string, entry: IndexEntry) => void;
|
|
12
|
+
removeEntry: (relativeFilePath: string) => void;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=dumpIndexHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dumpIndexHelper.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/dump/dumpIndexHelper.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiC1C,eAAO,MAAM,eAAe;4BACJ,OAAO;8BAYL,OAAO;yBAEZ,IAAI;2BASF,IAAI;yBAgCN,IAAI;iCAqBM,MAAM,KAAG,OAAO;iCAEhB,MAAM,KAAG,UAAU,GAAG,SAAS;;iCAK/B,MAAM,SAAS,UAAU,KAAG,IAAI;oCAK7B,MAAM,KAAG,IAAI;CAI9C,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.dumpIndexHelper = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const logger_1 = require("../../utils/logger");
|
|
9
|
+
const string_1 = require("../../utils/string");
|
|
10
|
+
const config_1 = require("../../config");
|
|
11
|
+
// import { Dump, DumpMetadata } from './dump.types';
|
|
12
|
+
// import { unixEpochUniqueId } from '../workDir';
|
|
13
|
+
const fs_2 = require("../../utils/fs");
|
|
14
|
+
const index = new Map();
|
|
15
|
+
let isStale = undefined;
|
|
16
|
+
const getIndexFilepath = () => `${config_1.config.dumpDir}/files.idx`;
|
|
17
|
+
const getIndexIsStaleFlagFilepath = () => `${config_1.config.dumpDir}/index-is-stale.flag`;
|
|
18
|
+
const indexToString = () => {
|
|
19
|
+
const indexLines = [];
|
|
20
|
+
index.forEach((value, filePath) => {
|
|
21
|
+
const line = [filePath, value.hash, value.url];
|
|
22
|
+
indexLines.push(line.join('\t'));
|
|
23
|
+
});
|
|
24
|
+
return indexLines.join('\n');
|
|
25
|
+
};
|
|
26
|
+
const setIsStale = (value) => {
|
|
27
|
+
if (isStale !== value) {
|
|
28
|
+
isStale = value;
|
|
29
|
+
const indexIsStaleFlagFilepath = getIndexIsStaleFlagFilepath();
|
|
30
|
+
if (isStale === true) {
|
|
31
|
+
fs_1.default.writeFileSync(indexIsStaleFlagFilepath, '');
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
if (fs_1.default.existsSync(indexIsStaleFlagFilepath)) {
|
|
35
|
+
fs_1.default.unlinkSync(indexIsStaleFlagFilepath);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
exports.dumpIndexHelper = {
|
|
41
|
+
isDumpIndexStale: () => {
|
|
42
|
+
if (isStale === undefined) {
|
|
43
|
+
const indexIsStaleFlagFilepath = getIndexIsStaleFlagFilepath();
|
|
44
|
+
if (fs_1.default.existsSync(indexIsStaleFlagFilepath)) {
|
|
45
|
+
setIsStale(true);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
setIsStale(false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return !!isStale;
|
|
52
|
+
},
|
|
53
|
+
isDumpIndexPresent: () => fs_1.default.existsSync(getIndexFilepath()),
|
|
54
|
+
saveDumpIndex: () => {
|
|
55
|
+
if (isStale) {
|
|
56
|
+
const indexFilepath = getIndexFilepath();
|
|
57
|
+
const indexAsString = indexToString().trim();
|
|
58
|
+
fs_1.default.writeFileSync(indexFilepath, indexAsString);
|
|
59
|
+
setIsStale(false);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
createDumpIndex: () => {
|
|
63
|
+
if (config_1.config.dumpDir) {
|
|
64
|
+
setIsStale(true);
|
|
65
|
+
logger_1.logger.info('Creating dump index...');
|
|
66
|
+
const startDate = Date.now();
|
|
67
|
+
index.clear();
|
|
68
|
+
const filesDumpDir = `${config_1.config.dumpDir}/files`;
|
|
69
|
+
const relativeFilePaths = (0, fs_2.findFilesInDir)(filesDumpDir);
|
|
70
|
+
relativeFilePaths.forEach(relativeFilePath => {
|
|
71
|
+
const fileContent = (0, fs_2.getFileContent)(`${filesDumpDir}/${relativeFilePath}`);
|
|
72
|
+
if (fileContent.raw) {
|
|
73
|
+
const url = fileContent.json
|
|
74
|
+
? fileContent.json.data?.content?.url?.path
|
|
75
|
+
: null;
|
|
76
|
+
const hash = (0, string_1.createHash)(fileContent.raw);
|
|
77
|
+
index.set(relativeFilePath, { hash, url });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Save index into disk
|
|
81
|
+
exports.dumpIndexHelper.saveDumpIndex();
|
|
82
|
+
logger_1.logger.info(`${relativeFilePaths.length} files in dump indexed in ${Date.now() - startDate}ms.`);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
loadDumpIndex: () => {
|
|
86
|
+
if (config_1.config.dumpDir) {
|
|
87
|
+
logger_1.logger.info('Loading dump index...');
|
|
88
|
+
const startDate = Date.now();
|
|
89
|
+
const indexFilepath = getIndexFilepath();
|
|
90
|
+
const data = fs_1.default.readFileSync(indexFilepath).toString().trim();
|
|
91
|
+
index.clear();
|
|
92
|
+
// Split lines and remove empty ones.
|
|
93
|
+
const dataLines = data.split('\n').filter(line => !!line);
|
|
94
|
+
dataLines.forEach(line => {
|
|
95
|
+
const [relativeFilePath, hash, url] = line.split('\t');
|
|
96
|
+
index.set(relativeFilePath, { hash, url });
|
|
97
|
+
});
|
|
98
|
+
logger_1.logger.info(`${dataLines.length} entries loaded from dump index in ${Date.now() - startDate}ms.`);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
hasEntry: (relativeFilePath) => index.has(relativeFilePath),
|
|
102
|
+
getEntry: (relativeFilePath) => index.get(relativeFilePath),
|
|
103
|
+
getKeys: () => index.keys(),
|
|
104
|
+
addEntry: (relativeFilePath, entry) => {
|
|
105
|
+
setIsStale(true);
|
|
106
|
+
index.set(relativeFilePath, entry);
|
|
107
|
+
},
|
|
108
|
+
removeEntry: (relativeFilePath) => {
|
|
109
|
+
setIsStale(true);
|
|
110
|
+
index.delete(relativeFilePath);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dumpManager.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/dump/dumpManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dumpManager.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/dump/dumpManager.ts"],"names":[],"mappings":"AASA,OAAO,EAAQ,WAAW,EAAE,MAAM,cAAc,CAAC;AAqKjD,eAAO,MAAM,WAAW,EAAE,WA+EzB,CAAC"}
|
|
@@ -14,59 +14,49 @@ const store_1 = require("../store");
|
|
|
14
14
|
const diffManager_1 = require("../diff/diffManager");
|
|
15
15
|
const hook_1 = require("../hook");
|
|
16
16
|
const dumpMetadataHelper_1 = require("./dumpMetadataHelper");
|
|
17
|
-
const
|
|
17
|
+
const dumpIndexHelper_1 = require("./dumpIndexHelper");
|
|
18
|
+
const string_1 = require("../../utils/string");
|
|
19
|
+
const storeUpdatedFiles = (diff, dump, filesDumpDir) => {
|
|
18
20
|
diff.updated.forEach(relativeFilepath => {
|
|
19
21
|
const storeFileContentData = store_1.store.data.get(relativeFilepath);
|
|
20
22
|
if (storeFileContentData) {
|
|
21
|
-
const absoluteFilepathInDumpDir = `${
|
|
23
|
+
const absoluteFilepathInDumpDir = `${filesDumpDir}/${relativeFilepath}`;
|
|
22
24
|
try {
|
|
23
|
-
// Create parent directories if they are missing.
|
|
24
|
-
const dir = path_1.default.dirname(absoluteFilepathInDumpDir);
|
|
25
|
-
if (!fs_1.default.existsSync(dir)) {
|
|
26
|
-
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
27
|
-
}
|
|
28
25
|
// If store data is an object, stringify it to execute its
|
|
29
26
|
// queries and resolve its includes.
|
|
30
27
|
const isJson = (0, fs_2.isJsonFile)(relativeFilepath);
|
|
31
28
|
const storeFileContentString = isJson
|
|
32
29
|
? JSON.stringify(storeFileContentData)
|
|
33
30
|
: storeFileContentData;
|
|
34
|
-
// Before overwriting a file, check it has changed.
|
|
31
|
+
// Before overwriting a file, check if it has changed.
|
|
35
32
|
let needsSave = true;
|
|
36
33
|
let oldPublicUrl = null;
|
|
37
34
|
const newPublicUrl = storeFileContentData.data?.content?.url?.path || null;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
const newHash = (0, string_1.createHash)(storeFileContentString);
|
|
36
|
+
const indexEntry = dumpIndexHelper_1.dumpIndexHelper.getEntry(relativeFilepath);
|
|
37
|
+
if (indexEntry) {
|
|
38
|
+
if (newHash === indexEntry.hash) {
|
|
41
39
|
// No need to save it, file has not changed.
|
|
42
40
|
needsSave = false;
|
|
43
41
|
}
|
|
44
|
-
else if (isJson
|
|
45
|
-
// Get old public URL before it changes on disk.
|
|
46
|
-
oldPublicUrl =
|
|
47
|
-
JSON.parse(dumpFileContentString)?.data?.content?.url?.path ||
|
|
48
|
-
null;
|
|
49
|
-
/*
|
|
50
|
-
// Save a copy for later reference.
|
|
51
|
-
const backupAbsoluteFilepathInDumpDir =
|
|
52
|
-
absoluteFilepathInDumpDir.replace(
|
|
53
|
-
'/dump/files/',
|
|
54
|
-
'/dump/files.bak/',
|
|
55
|
-
);
|
|
56
|
-
const backupDir = path.dirname(backupAbsoluteFilepathInDumpDir);
|
|
57
|
-
if (!fs.existsSync(backupDir)) {
|
|
58
|
-
fs.mkdirSync(backupDir, { recursive: true });
|
|
59
|
-
}
|
|
60
|
-
fs.renameSync(
|
|
61
|
-
absoluteFilepathInDumpDir,
|
|
62
|
-
backupAbsoluteFilepathInDumpDir,
|
|
63
|
-
);
|
|
64
|
-
*/
|
|
42
|
+
else if (isJson) {
|
|
43
|
+
// Get old public URL before it changes on disk and index.
|
|
44
|
+
oldPublicUrl = indexEntry.url;
|
|
65
45
|
}
|
|
66
46
|
}
|
|
67
47
|
if (needsSave) {
|
|
68
|
-
//
|
|
48
|
+
// Create parent directories if they are missing.
|
|
49
|
+
const dir = path_1.default.dirname(absoluteFilepathInDumpDir);
|
|
50
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
51
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
// Save it on disk.
|
|
69
54
|
fs_1.default.writeFileSync(absoluteFilepathInDumpDir, storeFileContentString);
|
|
55
|
+
// Save it to index.
|
|
56
|
+
dumpIndexHelper_1.dumpIndexHelper.addEntry(relativeFilepath, {
|
|
57
|
+
hash: newHash,
|
|
58
|
+
url: newPublicUrl,
|
|
59
|
+
});
|
|
70
60
|
// Mark it as updated.
|
|
71
61
|
dump.updated.set(relativeFilepath, {
|
|
72
62
|
oldPublicUrl,
|
|
@@ -83,18 +73,20 @@ const storeUpdatedFiles = (diff, dump, dumpDir) => {
|
|
|
83
73
|
}
|
|
84
74
|
});
|
|
85
75
|
};
|
|
86
|
-
const removeDeletedFiles = (diff, dump,
|
|
76
|
+
const removeDeletedFiles = (diff, dump, filesDumpDir) => {
|
|
87
77
|
diff.deleted.forEach(relativeFilepath => {
|
|
88
|
-
const absoluteFilepath = `${
|
|
78
|
+
const absoluteFilepath = `${filesDumpDir}/${relativeFilepath}`;
|
|
89
79
|
try {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const oldPublicUrl =
|
|
80
|
+
const indexEntry = dumpIndexHelper_1.dumpIndexHelper.getEntry(relativeFilepath);
|
|
81
|
+
if (indexEntry) {
|
|
82
|
+
// Get old public URLs before they change on disk and index.
|
|
83
|
+
const oldPublicUrl = indexEntry.url;
|
|
94
84
|
// Delete it.
|
|
95
85
|
fs_1.default.unlinkSync(absoluteFilepath);
|
|
96
86
|
// Delete any empty directory.
|
|
97
87
|
(0, fs_2.removeEmptyDirsUpwards)(path_1.default.dirname(absoluteFilepath));
|
|
88
|
+
// Delete it from index.
|
|
89
|
+
dumpIndexHelper_1.dumpIndexHelper.removeEntry(relativeFilepath);
|
|
98
90
|
// Mark it as deleted.
|
|
99
91
|
dump.deleted.set(relativeFilepath, {
|
|
100
92
|
oldPublicUrl,
|
|
@@ -107,6 +99,48 @@ const removeDeletedFiles = (diff, dump, dumpDir) => {
|
|
|
107
99
|
}
|
|
108
100
|
});
|
|
109
101
|
};
|
|
102
|
+
const removeStaleFiles = (diff, dump, filesDumpDir) => {
|
|
103
|
+
const iterator = dumpIndexHelper_1.dumpIndexHelper.getKeys();
|
|
104
|
+
for (const relativeFilepath of iterator) {
|
|
105
|
+
const absoluteFilepath = `${filesDumpDir}/${relativeFilepath}`;
|
|
106
|
+
if (!store_1.store.data.has(relativeFilepath) && fs_1.default.existsSync(absoluteFilepath)) {
|
|
107
|
+
logger_1.logger.info(`Dump: removing stale file: ${relativeFilepath}`);
|
|
108
|
+
try {
|
|
109
|
+
// Get old public URLs before they change on disk.
|
|
110
|
+
const dumpStaleFileString = fs_1.default
|
|
111
|
+
.readFileSync(absoluteFilepath)
|
|
112
|
+
.toString();
|
|
113
|
+
const isJson = (0, fs_2.isJsonFile)(relativeFilepath);
|
|
114
|
+
const dumpStaleFileContentData = isJson
|
|
115
|
+
? JSON.parse(dumpStaleFileString)
|
|
116
|
+
: dumpStaleFileString;
|
|
117
|
+
let oldPublicUrl = dumpStaleFileContentData.data?.content?.url?.path || null;
|
|
118
|
+
// An stale file can be a manually copied file.
|
|
119
|
+
// In that case, that stale file would have an oldPublicUrl from a valid file,
|
|
120
|
+
// which would lead to deleting a valid URL.
|
|
121
|
+
// To solve that case, check that oldPublicUrl is not a valid URL.
|
|
122
|
+
if (oldPublicUrl && store_1.store.index.url.has(oldPublicUrl)) {
|
|
123
|
+
logger_1.logger.info(`Dump: skipping valid url from stale file: ${oldPublicUrl}`);
|
|
124
|
+
oldPublicUrl = null;
|
|
125
|
+
}
|
|
126
|
+
// Delete it.
|
|
127
|
+
fs_1.default.unlinkSync(absoluteFilepath);
|
|
128
|
+
// Delete any empty directory.
|
|
129
|
+
(0, fs_2.removeEmptyDirsUpwards)(path_1.default.dirname(absoluteFilepath));
|
|
130
|
+
// Delete it from index.
|
|
131
|
+
dumpIndexHelper_1.dumpIndexHelper.removeEntry(relativeFilepath);
|
|
132
|
+
// Mark it as deleted.
|
|
133
|
+
dump.deleted.set(relativeFilepath, {
|
|
134
|
+
oldPublicUrl,
|
|
135
|
+
newPublicUrl: null,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
logger_1.logger.error(`Dump: error deleting stale file "${absoluteFilepath}": ${e}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
110
144
|
exports.dumpManager = {
|
|
111
145
|
dump(options = { incremental: true }) {
|
|
112
146
|
const startDate = microtime_1.default.now();
|
|
@@ -121,12 +155,17 @@ exports.dumpManager = {
|
|
|
121
155
|
diff,
|
|
122
156
|
};
|
|
123
157
|
if (config_1.config.dumpDir) {
|
|
124
|
-
const
|
|
158
|
+
const filesDumpDir = `${config_1.config.dumpDir}/files`;
|
|
125
159
|
if (diff.updated.size || diff.deleted.size) {
|
|
160
|
+
dumpMetadataHelper_1.dumpMetadataHelper.storeDumpMetadata(dump);
|
|
126
161
|
// Store updated files.
|
|
127
|
-
storeUpdatedFiles(diff, dump,
|
|
162
|
+
storeUpdatedFiles(diff, dump, filesDumpDir);
|
|
128
163
|
// Remove deleted files.
|
|
129
|
-
removeDeletedFiles(diff, dump,
|
|
164
|
+
removeDeletedFiles(diff, dump, filesDumpDir);
|
|
165
|
+
// Remove stale files.
|
|
166
|
+
removeStaleFiles(diff, dump, filesDumpDir);
|
|
167
|
+
// Save dump index
|
|
168
|
+
dumpIndexHelper_1.dumpIndexHelper.saveDumpIndex();
|
|
130
169
|
// Invoke "onDumpCreate" hook.
|
|
131
170
|
dump = hook_1.hookManager.invokeOnDumpCreate(dump);
|
|
132
171
|
const execTimeMs = (microtime_1.default.now() - startDate) / 1000;
|
|
@@ -140,12 +179,6 @@ exports.dumpManager = {
|
|
|
140
179
|
diffManager_1.diffManager.reset(diff.toUniqueId);
|
|
141
180
|
}
|
|
142
181
|
logger_1.logger.info(`Dump created in ${execTimeMs} ms. Updated: ${dump.updated.size} / Deleted: ${dump.deleted.size}`);
|
|
143
|
-
/*
|
|
144
|
-
// Log dump if not empty
|
|
145
|
-
if (dump.updated.size || dump.deleted.size) {
|
|
146
|
-
logger.debug(`Dump: "${JSON.stringify(jsonify(dump))}"`);
|
|
147
|
-
}
|
|
148
|
-
*/
|
|
149
182
|
}
|
|
150
183
|
else {
|
|
151
184
|
// Resetting the diff must happen when no other operations are pending.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dumpMetadataHelper.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/dump/dumpMetadataHelper.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKlD,eAAO,MAAM,kBAAkB;8BACH,IAAI,KAAG,OAAO;sCA6BN,MAAM,GAAG,YAAY;8BAuB7B,MAAM;
|
|
1
|
+
{"version":3,"file":"dumpMetadataHelper.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/dump/dumpMetadataHelper.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKlD,eAAO,MAAM,kBAAkB;8BACH,IAAI,KAAG,OAAO;sCA6BN,MAAM,GAAG,YAAY;8BAuB7B,MAAM;CAYjC,CAAC"}
|
|
@@ -54,6 +54,7 @@ exports.dumpMetadataHelper = {
|
|
|
54
54
|
},
|
|
55
55
|
getCurrentDumpUniqueId() {
|
|
56
56
|
const metadataFilepath = getMetadataFilepath();
|
|
57
|
+
logger_1.logger.debug(`metadataFilepath: ${metadataFilepath}`);
|
|
57
58
|
const metadata = JSON.parse(fs_1.default.existsSync(metadataFilepath)
|
|
58
59
|
? fs_1.default.readFileSync(metadataFilepath).toString()
|
|
59
60
|
: '[]');
|
|
@@ -48,4 +48,10 @@ export declare const parseUniqueId: (uniqueId: string) => Date | null;
|
|
|
48
48
|
* @param uniqueId - A unique id to be checked
|
|
49
49
|
*/
|
|
50
50
|
export declare const isUniqueId: (uniqueId: string) => boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Creates a hash from a string
|
|
53
|
+
*
|
|
54
|
+
* @param data - A string of data
|
|
55
|
+
*/
|
|
56
|
+
export declare const createHash: (data: string) => string;
|
|
51
57
|
//# sourceMappingURL=stringUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stringUtils.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/string/stringUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,YAAY,MAAM,KAAG,GAQpD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAC/B,aAAa,MAAM,KAClB,qBAWF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,KAAG,IAAI,GAAG,IAevD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,KAAG,OAC+B,CAAC"}
|
|
1
|
+
{"version":3,"file":"stringUtils.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/string/stringUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,YAAY,MAAM,KAAG,GAQpD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAC/B,aAAa,MAAM,KAClB,qBAWF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,KAAG,IAAI,GAAG,IAevD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,KAAG,OAC+B,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,MAC0B,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isUniqueId = exports.parseUniqueId = exports.parseURLSearchParams = exports.parseJsonString = exports.VARIANT_SEPARATOR = void 0;
|
|
3
|
+
exports.createHash = exports.isUniqueId = exports.parseUniqueId = exports.parseURLSearchParams = exports.parseJsonString = exports.VARIANT_SEPARATOR = void 0;
|
|
4
4
|
const logger_1 = require("../logger");
|
|
5
5
|
/**
|
|
6
6
|
* Separator for variant data files saved to storage.
|
|
@@ -87,3 +87,10 @@ exports.parseUniqueId = parseUniqueId;
|
|
|
87
87
|
*/
|
|
88
88
|
const isUniqueId = (uniqueId) => /(\d{4})-(\d{2})-(\d{2})_(\d{2})-(\d{2})-(\d{2})\.(\d{3}).*/.test(uniqueId);
|
|
89
89
|
exports.isUniqueId = isUniqueId;
|
|
90
|
+
/**
|
|
91
|
+
* Creates a hash from a string
|
|
92
|
+
*
|
|
93
|
+
* @param data - A string of data
|
|
94
|
+
*/
|
|
95
|
+
const createHash = (data) => require('crypto').createHash('sha1').update(data).digest('base64');
|
|
96
|
+
exports.createHash = createHash;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@static-suite/static-suite-data-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "A flexible, fast and developer friendly Data Server for Static Suite",
|
|
5
5
|
"main": "./dist/lib/index.js",
|
|
6
6
|
"typings": "./dist/lib/index.d.ts",
|
|
@@ -84,5 +84,6 @@
|
|
|
84
84
|
"src/__tests__/fixtures/*",
|
|
85
85
|
"src/__tests__/mocks/*"
|
|
86
86
|
]
|
|
87
|
-
}
|
|
87
|
+
},
|
|
88
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
88
89
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { diffManager } from '../../lib/store/diff';
|
|
3
|
+
import { jsonify } from '../../lib/utils/object';
|
|
4
|
+
|
|
5
|
+
const diffIndex = (req: Request, res: Response): void => {
|
|
6
|
+
res.render('diffIndex', {
|
|
7
|
+
links: {
|
|
8
|
+
'/diff/incremental': 'Execute an incremental dump',
|
|
9
|
+
'/diff/full': 'Execute a full dump',
|
|
10
|
+
'/diff/reset':
|
|
11
|
+
'Clears intermediate changes tracked by dependency manager',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const diffAction = (
|
|
17
|
+
req: Request,
|
|
18
|
+
res: Response,
|
|
19
|
+
incremental: boolean,
|
|
20
|
+
): void => {
|
|
21
|
+
const diff = diffManager.getDiff({ incremental });
|
|
22
|
+
const diffAsJson: any = jsonify(diff);
|
|
23
|
+
|
|
24
|
+
res.status(200);
|
|
25
|
+
res.set({ 'Content-Type': 'application/json' });
|
|
26
|
+
res.send(diffAsJson);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const diffIncremental = (req: Request, res: Response): void => {
|
|
30
|
+
diffAction(req, res, true);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const diffFull = (req: Request, res: Response): void => {
|
|
34
|
+
diffAction(req, res, false);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const diffReset = (req: Request, res: Response): void => {
|
|
38
|
+
const args: any = req.query;
|
|
39
|
+
if (args?.uniqueId) {
|
|
40
|
+
diffManager.reset(args.uniqueId);
|
|
41
|
+
}
|
|
42
|
+
res.status(200);
|
|
43
|
+
res.set({ 'Content-Type': 'application/json' });
|
|
44
|
+
res.send({ status: 'done' });
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export { diffIndex, diffIncremental, diffFull, diffReset };
|
package/src/http/routes/home.ts
CHANGED
|
@@ -5,6 +5,7 @@ export const home = (req: Request, res: Response): void => {
|
|
|
5
5
|
links: {
|
|
6
6
|
'/data': 'Browse all data loaded into the server',
|
|
7
7
|
'/dependency': 'Dependency data',
|
|
8
|
+
'/diff': 'Diff operations',
|
|
8
9
|
'/dump': 'Dump operations',
|
|
9
10
|
'/query': 'List of available queries',
|
|
10
11
|
'/task': 'List of available tasks',
|
package/src/http/routes/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
import { indexUrl } from './indexUrl';
|
|
33
33
|
import { indexUUID } from './indexUUID';
|
|
34
34
|
import { subset } from './subset';
|
|
35
|
+
import { diffFull, diffIncremental, diffIndex, diffReset } from './diff';
|
|
35
36
|
|
|
36
37
|
const routes = Router();
|
|
37
38
|
|
|
@@ -49,6 +50,10 @@ routes.get('/task', taskIndex);
|
|
|
49
50
|
routes.get('/task/:taskId', runTask);
|
|
50
51
|
routes.get('/cache', cacheIndex);
|
|
51
52
|
routes.get('/cache/:cacheBinId/clear', cacheClear);
|
|
53
|
+
routes.get('/diff', diffIndex);
|
|
54
|
+
routes.get('/diff/incremental', diffIncremental);
|
|
55
|
+
routes.get('/diff/full', diffFull);
|
|
56
|
+
routes.get('/diff/reset', diffReset);
|
|
52
57
|
routes.get('/dump', dumpIndex);
|
|
53
58
|
routes.get('/dump/incremental', dumpIncremental);
|
|
54
59
|
routes.get('/dump/full', dumpFull);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Request, Response } from 'express';
|
|
2
|
-
import { StoreSubsetOptions } from 'lib/store/store.types';
|
|
2
|
+
import { StoreSubsetOptions } from '../../lib/store/store.types';
|
|
3
3
|
import { store } from '../../lib/store';
|
|
4
4
|
import { dataDirManager } from '../../lib/store/dataDir';
|
|
5
5
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
extends includes/layout.pug
|
|
2
|
+
|
|
3
|
+
block vars
|
|
4
|
+
- var title = "Diff | Static Suite Data Server"
|
|
5
|
+
|
|
6
|
+
block content
|
|
7
|
+
h1(class="title is-1")="Diff"
|
|
8
|
+
table(class="table is-fullwidth is-hoverable")
|
|
9
|
+
tbody
|
|
10
|
+
each description, link in links
|
|
11
|
+
tr
|
|
12
|
+
td
|
|
13
|
+
a(href=link)= link
|
|
14
|
+
td=description
|
package/src/lib/dataServer.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { configureLogger } from './utils/logger';
|
|
1
|
+
import { configureLogger, logger } from './utils/logger';
|
|
2
2
|
import { LogLevel, LogFile } from './utils/logger/logger.types';
|
|
3
3
|
import { initWatcher } from './watcher';
|
|
4
4
|
import { setConfig } from './config';
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
DataServerReturn,
|
|
22
22
|
DataServerInitOptions,
|
|
23
23
|
} from './dataServer.types';
|
|
24
|
+
import { dumpIndexHelper } from './store/dump/dumpIndexHelper';
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* The Data Server instance.
|
|
@@ -36,6 +37,8 @@ const dataServer = {
|
|
|
36
37
|
* @returns An object with the data store and the queryRunner service.
|
|
37
38
|
*/
|
|
38
39
|
init: (options: DataServerInitOptions): DataServerReturn => {
|
|
40
|
+
const startDate = Date.now();
|
|
41
|
+
|
|
39
42
|
// Configure logger.
|
|
40
43
|
configureLogger(options.logLevel, options.logFile);
|
|
41
44
|
|
|
@@ -58,6 +61,19 @@ const dataServer = {
|
|
|
58
61
|
// Load data from dataDir.
|
|
59
62
|
dataDirManager.load();
|
|
60
63
|
|
|
64
|
+
// Create and load dump index.
|
|
65
|
+
if (config.dumpDir) {
|
|
66
|
+
if (
|
|
67
|
+
dumpIndexHelper.isDumpIndexStale() ||
|
|
68
|
+
!dumpIndexHelper.isDumpIndexPresent()
|
|
69
|
+
) {
|
|
70
|
+
dumpIndexHelper.createDumpIndex();
|
|
71
|
+
}
|
|
72
|
+
dumpIndexHelper.loadDumpIndex();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
logger.info(`Data Server loaded in ${Date.now() - startDate}ms.`);
|
|
76
|
+
|
|
61
77
|
return {
|
|
62
78
|
store,
|
|
63
79
|
queryRunner,
|
|
@@ -27,6 +27,7 @@ export const diffManager: DiffManager = {
|
|
|
27
27
|
const currentDumpUniqueId = dumpMetadataHelper.getCurrentDumpUniqueId();
|
|
28
28
|
const isFirstDiffAfterReboot = lastDiffUniqueId === unixEpochUniqueId;
|
|
29
29
|
const isADumpAvailable = currentDumpUniqueId !== unixEpochUniqueId;
|
|
30
|
+
|
|
30
31
|
// If both checks passes, check if something has changed after last dump to avoid a full diff:
|
|
31
32
|
if (isFirstDiffAfterReboot && isADumpAvailable) {
|
|
32
33
|
const dataDirModificationUniqueId = changedFiles.toUniqueId;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { logger } from '../../utils/logger';
|
|
3
|
+
import { createHash } from '../../utils/string';
|
|
4
|
+
import { config } from '../../config';
|
|
5
|
+
// import { Dump, DumpMetadata } from './dump.types';
|
|
6
|
+
// import { unixEpochUniqueId } from '../workDir';
|
|
7
|
+
import { findFilesInDir, getFileContent } from '../../utils/fs';
|
|
8
|
+
import { IndexEntry } from './dump.types';
|
|
9
|
+
|
|
10
|
+
const index = new Map<string, IndexEntry>();
|
|
11
|
+
|
|
12
|
+
let isStale: boolean | undefined = undefined;
|
|
13
|
+
|
|
14
|
+
const getIndexFilepath = () => `${config.dumpDir}/files.idx`;
|
|
15
|
+
const getIndexIsStaleFlagFilepath = () =>
|
|
16
|
+
`${config.dumpDir}/index-is-stale.flag`;
|
|
17
|
+
|
|
18
|
+
const indexToString = (): string => {
|
|
19
|
+
const indexLines: string[] = [];
|
|
20
|
+
index.forEach((value, filePath) => {
|
|
21
|
+
const line = [filePath, value.hash, value.url];
|
|
22
|
+
indexLines.push(line.join('\t'));
|
|
23
|
+
});
|
|
24
|
+
return indexLines.join('\n');
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const setIsStale = (value: boolean): void => {
|
|
28
|
+
if (isStale !== value) {
|
|
29
|
+
isStale = value;
|
|
30
|
+
const indexIsStaleFlagFilepath = getIndexIsStaleFlagFilepath();
|
|
31
|
+
if (isStale === true) {
|
|
32
|
+
fs.writeFileSync(indexIsStaleFlagFilepath, '');
|
|
33
|
+
} else {
|
|
34
|
+
if (fs.existsSync(indexIsStaleFlagFilepath)) {
|
|
35
|
+
fs.unlinkSync(indexIsStaleFlagFilepath);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const dumpIndexHelper = {
|
|
42
|
+
isDumpIndexStale: (): boolean => {
|
|
43
|
+
if (isStale === undefined) {
|
|
44
|
+
const indexIsStaleFlagFilepath = getIndexIsStaleFlagFilepath();
|
|
45
|
+
if (fs.existsSync(indexIsStaleFlagFilepath)) {
|
|
46
|
+
setIsStale(true);
|
|
47
|
+
} else {
|
|
48
|
+
setIsStale(false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return !!isStale;
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
isDumpIndexPresent: (): boolean => fs.existsSync(getIndexFilepath()),
|
|
55
|
+
|
|
56
|
+
saveDumpIndex: (): void => {
|
|
57
|
+
if (isStale) {
|
|
58
|
+
const indexFilepath = getIndexFilepath();
|
|
59
|
+
const indexAsString = indexToString().trim();
|
|
60
|
+
fs.writeFileSync(indexFilepath, indexAsString);
|
|
61
|
+
setIsStale(false);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
createDumpIndex: (): void => {
|
|
66
|
+
if (config.dumpDir) {
|
|
67
|
+
setIsStale(true);
|
|
68
|
+
logger.info('Creating dump index...');
|
|
69
|
+
const startDate = Date.now();
|
|
70
|
+
index.clear();
|
|
71
|
+
const filesDumpDir = `${config.dumpDir}/files`;
|
|
72
|
+
const relativeFilePaths = findFilesInDir(filesDumpDir);
|
|
73
|
+
relativeFilePaths.forEach(relativeFilePath => {
|
|
74
|
+
const fileContent = getFileContent(
|
|
75
|
+
`${filesDumpDir}/${relativeFilePath}`,
|
|
76
|
+
);
|
|
77
|
+
if (fileContent.raw) {
|
|
78
|
+
const url = fileContent.json
|
|
79
|
+
? fileContent.json.data?.content?.url?.path
|
|
80
|
+
: null;
|
|
81
|
+
const hash = createHash(fileContent.raw);
|
|
82
|
+
index.set(relativeFilePath, { hash, url });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Save index into disk
|
|
87
|
+
dumpIndexHelper.saveDumpIndex();
|
|
88
|
+
|
|
89
|
+
logger.info(
|
|
90
|
+
`${relativeFilePaths.length} files in dump indexed in ${
|
|
91
|
+
Date.now() - startDate
|
|
92
|
+
}ms.`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
loadDumpIndex: (): void => {
|
|
98
|
+
if (config.dumpDir) {
|
|
99
|
+
logger.info('Loading dump index...');
|
|
100
|
+
const startDate = Date.now();
|
|
101
|
+
const indexFilepath = getIndexFilepath();
|
|
102
|
+
const data = fs.readFileSync(indexFilepath).toString().trim();
|
|
103
|
+
index.clear();
|
|
104
|
+
// Split lines and remove empty ones.
|
|
105
|
+
const dataLines: string[] = data.split('\n').filter(line => !!line);
|
|
106
|
+
dataLines.forEach(line => {
|
|
107
|
+
const [relativeFilePath, hash, url] = line.split('\t');
|
|
108
|
+
index.set(relativeFilePath, { hash, url });
|
|
109
|
+
});
|
|
110
|
+
logger.info(
|
|
111
|
+
`${dataLines.length} entries loaded from dump index in ${
|
|
112
|
+
Date.now() - startDate
|
|
113
|
+
}ms.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
hasEntry: (relativeFilePath: string): boolean => index.has(relativeFilePath),
|
|
119
|
+
|
|
120
|
+
getEntry: (relativeFilePath: string): IndexEntry | undefined =>
|
|
121
|
+
index.get(relativeFilePath),
|
|
122
|
+
|
|
123
|
+
getKeys: () => index.keys(),
|
|
124
|
+
|
|
125
|
+
addEntry: (relativeFilePath: string, entry: IndexEntry): void => {
|
|
126
|
+
setIsStale(true);
|
|
127
|
+
index.set(relativeFilePath, entry);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
removeEntry: (relativeFilePath: string): void => {
|
|
131
|
+
setIsStale(true);
|
|
132
|
+
index.delete(relativeFilePath);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
@@ -2,12 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import microtime from 'microtime';
|
|
4
4
|
import { logger } from '../../utils/logger';
|
|
5
|
-
import {
|
|
6
|
-
getFileContent,
|
|
7
|
-
isJsonFile,
|
|
8
|
-
readFile,
|
|
9
|
-
removeEmptyDirsUpwards,
|
|
10
|
-
} from '../../utils/fs';
|
|
5
|
+
import { isJsonFile, removeEmptyDirsUpwards } from '../../utils/fs';
|
|
11
6
|
import { config } from '../../config';
|
|
12
7
|
import { store } from '../store';
|
|
13
8
|
import { diffManager } from '../diff/diffManager';
|
|
@@ -15,19 +10,19 @@ import { Diff } from '../diff/diff.types';
|
|
|
15
10
|
import { Dump, DumpManager } from './dump.types';
|
|
16
11
|
import { hookManager } from '../hook';
|
|
17
12
|
import { dumpMetadataHelper } from './dumpMetadataHelper';
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
import { dumpIndexHelper } from './dumpIndexHelper';
|
|
14
|
+
import { createHash } from '../../utils/string';
|
|
15
|
+
|
|
16
|
+
const storeUpdatedFiles = (
|
|
17
|
+
diff: Diff,
|
|
18
|
+
dump: Dump,
|
|
19
|
+
filesDumpDir: string,
|
|
20
|
+
): void => {
|
|
20
21
|
diff.updated.forEach(relativeFilepath => {
|
|
21
22
|
const storeFileContentData = store.data.get(relativeFilepath);
|
|
22
23
|
if (storeFileContentData) {
|
|
23
|
-
const absoluteFilepathInDumpDir = `${
|
|
24
|
+
const absoluteFilepathInDumpDir = `${filesDumpDir}/${relativeFilepath}`;
|
|
24
25
|
try {
|
|
25
|
-
// Create parent directories if they are missing.
|
|
26
|
-
const dir = path.dirname(absoluteFilepathInDumpDir);
|
|
27
|
-
if (!fs.existsSync(dir)) {
|
|
28
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
26
|
// If store data is an object, stringify it to execute its
|
|
32
27
|
// queries and resolve its includes.
|
|
33
28
|
const isJson = isJsonFile(relativeFilepath);
|
|
@@ -35,43 +30,39 @@ const storeUpdatedFiles = (diff: Diff, dump: Dump, dumpDir: string): void => {
|
|
|
35
30
|
? JSON.stringify(storeFileContentData)
|
|
36
31
|
: storeFileContentData;
|
|
37
32
|
|
|
38
|
-
// Before overwriting a file, check it has changed.
|
|
33
|
+
// Before overwriting a file, check if it has changed.
|
|
39
34
|
let needsSave = true;
|
|
40
35
|
let oldPublicUrl: string | null = null;
|
|
41
36
|
const newPublicUrl: string | null =
|
|
42
37
|
storeFileContentData.data?.content?.url?.path || null;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
const newHash = createHash(storeFileContentString);
|
|
39
|
+
const indexEntry = dumpIndexHelper.getEntry(relativeFilepath);
|
|
40
|
+
if (indexEntry) {
|
|
41
|
+
if (newHash === indexEntry.hash) {
|
|
46
42
|
// No need to save it, file has not changed.
|
|
47
43
|
needsSave = false;
|
|
48
|
-
} else if (isJson
|
|
49
|
-
// Get old public URL before it changes on disk.
|
|
50
|
-
oldPublicUrl =
|
|
51
|
-
JSON.parse(dumpFileContentString)?.data?.content?.url?.path ||
|
|
52
|
-
null;
|
|
53
|
-
/*
|
|
54
|
-
// Save a copy for later reference.
|
|
55
|
-
const backupAbsoluteFilepathInDumpDir =
|
|
56
|
-
absoluteFilepathInDumpDir.replace(
|
|
57
|
-
'/dump/files/',
|
|
58
|
-
'/dump/files.bak/',
|
|
59
|
-
);
|
|
60
|
-
const backupDir = path.dirname(backupAbsoluteFilepathInDumpDir);
|
|
61
|
-
if (!fs.existsSync(backupDir)) {
|
|
62
|
-
fs.mkdirSync(backupDir, { recursive: true });
|
|
63
|
-
}
|
|
64
|
-
fs.renameSync(
|
|
65
|
-
absoluteFilepathInDumpDir,
|
|
66
|
-
backupAbsoluteFilepathInDumpDir,
|
|
67
|
-
);
|
|
68
|
-
*/
|
|
44
|
+
} else if (isJson) {
|
|
45
|
+
// Get old public URL before it changes on disk and index.
|
|
46
|
+
oldPublicUrl = indexEntry.url;
|
|
69
47
|
}
|
|
70
48
|
}
|
|
49
|
+
|
|
71
50
|
if (needsSave) {
|
|
72
|
-
//
|
|
51
|
+
// Create parent directories if they are missing.
|
|
52
|
+
const dir = path.dirname(absoluteFilepathInDumpDir);
|
|
53
|
+
if (!fs.existsSync(dir)) {
|
|
54
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Save it on disk.
|
|
73
58
|
fs.writeFileSync(absoluteFilepathInDumpDir, storeFileContentString);
|
|
74
59
|
|
|
60
|
+
// Save it to index.
|
|
61
|
+
dumpIndexHelper.addEntry(relativeFilepath, {
|
|
62
|
+
hash: newHash,
|
|
63
|
+
url: newPublicUrl,
|
|
64
|
+
});
|
|
65
|
+
|
|
75
66
|
// Mark it as updated.
|
|
76
67
|
dump.updated.set(relativeFilepath, {
|
|
77
68
|
oldPublicUrl,
|
|
@@ -91,15 +82,18 @@ const storeUpdatedFiles = (diff: Diff, dump: Dump, dumpDir: string): void => {
|
|
|
91
82
|
});
|
|
92
83
|
};
|
|
93
84
|
|
|
94
|
-
const removeDeletedFiles = (
|
|
85
|
+
const removeDeletedFiles = (
|
|
86
|
+
diff: Diff,
|
|
87
|
+
dump: Dump,
|
|
88
|
+
filesDumpDir: string,
|
|
89
|
+
): void => {
|
|
95
90
|
diff.deleted.forEach(relativeFilepath => {
|
|
96
|
-
const absoluteFilepath = `${
|
|
91
|
+
const absoluteFilepath = `${filesDumpDir}/${relativeFilepath}`;
|
|
97
92
|
try {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const oldPublicUrl
|
|
102
|
-
dumpContent.json?.data?.content?.url?.path;
|
|
93
|
+
const indexEntry = dumpIndexHelper.getEntry(relativeFilepath);
|
|
94
|
+
if (indexEntry) {
|
|
95
|
+
// Get old public URLs before they change on disk and index.
|
|
96
|
+
const oldPublicUrl = indexEntry.url;
|
|
103
97
|
|
|
104
98
|
// Delete it.
|
|
105
99
|
fs.unlinkSync(absoluteFilepath);
|
|
@@ -107,6 +101,9 @@ const removeDeletedFiles = (diff: Diff, dump: Dump, dumpDir: string): void => {
|
|
|
107
101
|
// Delete any empty directory.
|
|
108
102
|
removeEmptyDirsUpwards(path.dirname(absoluteFilepath));
|
|
109
103
|
|
|
104
|
+
// Delete it from index.
|
|
105
|
+
dumpIndexHelper.removeEntry(relativeFilepath);
|
|
106
|
+
|
|
110
107
|
// Mark it as deleted.
|
|
111
108
|
dump.deleted.set(relativeFilepath, {
|
|
112
109
|
oldPublicUrl,
|
|
@@ -119,6 +116,62 @@ const removeDeletedFiles = (diff: Diff, dump: Dump, dumpDir: string): void => {
|
|
|
119
116
|
});
|
|
120
117
|
};
|
|
121
118
|
|
|
119
|
+
const removeStaleFiles = (
|
|
120
|
+
diff: Diff,
|
|
121
|
+
dump: Dump,
|
|
122
|
+
filesDumpDir: string,
|
|
123
|
+
): void => {
|
|
124
|
+
const iterator = dumpIndexHelper.getKeys();
|
|
125
|
+
for (const relativeFilepath of iterator) {
|
|
126
|
+
const absoluteFilepath = `${filesDumpDir}/${relativeFilepath}`;
|
|
127
|
+
if (!store.data.has(relativeFilepath) && fs.existsSync(absoluteFilepath)) {
|
|
128
|
+
logger.info(`Dump: removing stale file: ${relativeFilepath}`);
|
|
129
|
+
try {
|
|
130
|
+
// Get old public URLs before they change on disk.
|
|
131
|
+
const dumpStaleFileString = fs
|
|
132
|
+
.readFileSync(absoluteFilepath)
|
|
133
|
+
.toString();
|
|
134
|
+
const isJson = isJsonFile(relativeFilepath);
|
|
135
|
+
const dumpStaleFileContentData = isJson
|
|
136
|
+
? JSON.parse(dumpStaleFileString)
|
|
137
|
+
: dumpStaleFileString;
|
|
138
|
+
let oldPublicUrl: string | null =
|
|
139
|
+
dumpStaleFileContentData.data?.content?.url?.path || null;
|
|
140
|
+
|
|
141
|
+
// An stale file can be a manually copied file.
|
|
142
|
+
// In that case, that stale file would have an oldPublicUrl from a valid file,
|
|
143
|
+
// which would lead to deleting a valid URL.
|
|
144
|
+
// To solve that case, check that oldPublicUrl is not a valid URL.
|
|
145
|
+
if (oldPublicUrl && store.index.url.has(oldPublicUrl)) {
|
|
146
|
+
logger.info(
|
|
147
|
+
`Dump: skipping valid url from stale file: ${oldPublicUrl}`,
|
|
148
|
+
);
|
|
149
|
+
oldPublicUrl = null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Delete it.
|
|
153
|
+
fs.unlinkSync(absoluteFilepath);
|
|
154
|
+
|
|
155
|
+
// Delete any empty directory.
|
|
156
|
+
removeEmptyDirsUpwards(path.dirname(absoluteFilepath));
|
|
157
|
+
|
|
158
|
+
// Delete it from index.
|
|
159
|
+
dumpIndexHelper.removeEntry(relativeFilepath);
|
|
160
|
+
|
|
161
|
+
// Mark it as deleted.
|
|
162
|
+
dump.deleted.set(relativeFilepath, {
|
|
163
|
+
oldPublicUrl,
|
|
164
|
+
newPublicUrl: null,
|
|
165
|
+
});
|
|
166
|
+
} catch (e) {
|
|
167
|
+
logger.error(
|
|
168
|
+
`Dump: error deleting stale file "${absoluteFilepath}": ${e}`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
122
175
|
export const dumpManager: DumpManager = {
|
|
123
176
|
dump(options = { incremental: true }): Dump {
|
|
124
177
|
const startDate = microtime.now();
|
|
@@ -135,14 +188,22 @@ export const dumpManager: DumpManager = {
|
|
|
135
188
|
};
|
|
136
189
|
|
|
137
190
|
if (config.dumpDir) {
|
|
138
|
-
const
|
|
191
|
+
const filesDumpDir = `${config.dumpDir}/files`;
|
|
139
192
|
|
|
140
193
|
if (diff.updated.size || diff.deleted.size) {
|
|
194
|
+
dumpMetadataHelper.storeDumpMetadata(dump);
|
|
195
|
+
|
|
141
196
|
// Store updated files.
|
|
142
|
-
storeUpdatedFiles(diff, dump,
|
|
197
|
+
storeUpdatedFiles(diff, dump, filesDumpDir);
|
|
143
198
|
|
|
144
199
|
// Remove deleted files.
|
|
145
|
-
removeDeletedFiles(diff, dump,
|
|
200
|
+
removeDeletedFiles(diff, dump, filesDumpDir);
|
|
201
|
+
|
|
202
|
+
// Remove stale files.
|
|
203
|
+
removeStaleFiles(diff, dump, filesDumpDir);
|
|
204
|
+
|
|
205
|
+
// Save dump index
|
|
206
|
+
dumpIndexHelper.saveDumpIndex();
|
|
146
207
|
|
|
147
208
|
// Invoke "onDumpCreate" hook.
|
|
148
209
|
dump = hookManager.invokeOnDumpCreate(dump);
|
|
@@ -162,13 +223,6 @@ export const dumpManager: DumpManager = {
|
|
|
162
223
|
logger.info(
|
|
163
224
|
`Dump created in ${execTimeMs} ms. Updated: ${dump.updated.size} / Deleted: ${dump.deleted.size}`,
|
|
164
225
|
);
|
|
165
|
-
|
|
166
|
-
/*
|
|
167
|
-
// Log dump if not empty
|
|
168
|
-
if (dump.updated.size || dump.deleted.size) {
|
|
169
|
-
logger.debug(`Dump: "${JSON.stringify(jsonify(dump))}"`);
|
|
170
|
-
}
|
|
171
|
-
*/
|
|
172
226
|
} else {
|
|
173
227
|
// Resetting the diff must happen when no other operations are pending.
|
|
174
228
|
diffManager.reset(diff.toUniqueId);
|
|
@@ -63,6 +63,7 @@ export const dumpMetadataHelper = {
|
|
|
63
63
|
getCurrentDumpUniqueId(): string {
|
|
64
64
|
const metadataFilepath = getMetadataFilepath();
|
|
65
65
|
|
|
66
|
+
logger.debug(`metadataFilepath: ${metadataFilepath}`);
|
|
66
67
|
const metadata = JSON.parse(
|
|
67
68
|
fs.existsSync(metadataFilepath)
|
|
68
69
|
? fs.readFileSync(metadataFilepath).toString()
|
|
@@ -91,3 +91,11 @@ export const parseUniqueId = (uniqueId: string): Date | null => {
|
|
|
91
91
|
*/
|
|
92
92
|
export const isUniqueId = (uniqueId: string): boolean =>
|
|
93
93
|
/(\d{4})-(\d{2})-(\d{2})_(\d{2})-(\d{2})-(\d{2})\.(\d{3}).*/.test(uniqueId);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates a hash from a string
|
|
97
|
+
*
|
|
98
|
+
* @param data - A string of data
|
|
99
|
+
*/
|
|
100
|
+
export const createHash = (data: string): string =>
|
|
101
|
+
require('crypto').createHash('sha1').update(data).digest('base64');
|
package/tsconfig.json
CHANGED
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
|
|
31
31
|
/* Strict Type-Checking Options */
|
|
32
32
|
"strict": true /* Enable all strict type-checking options. */,
|
|
33
|
-
"paths": {
|
|
33
|
+
/* "paths": {
|
|
34
34
|
"*": ["./src/*"]
|
|
35
|
-
} /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'.
|
|
35
|
+
} */ /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
|
36
36
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
|
37
37
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
|
38
38
|
"types": [
|