@static-suite/static-suite-data-server 1.0.3 → 1.0.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.
Files changed (41) hide show
  1. package/dist/http/routes/diff.d.ts +7 -0
  2. package/dist/http/routes/diff.d.ts.map +1 -0
  3. package/dist/http/routes/diff.js +40 -0
  4. package/dist/http/routes/home.d.ts.map +1 -1
  5. package/dist/http/routes/home.js +1 -0
  6. package/dist/http/routes/index.d.ts.map +1 -1
  7. package/dist/http/routes/index.js +5 -0
  8. package/dist/http/views/diffIndex.pug +14 -0
  9. package/dist/lib/dataServer.d.ts.map +1 -1
  10. package/dist/lib/dataServer.js +11 -0
  11. package/dist/lib/store/diff/diff.types.d.ts.map +1 -1
  12. package/dist/lib/store/diff/diffManager.d.ts.map +1 -1
  13. package/dist/lib/store/dump/dump.types.d.ts +4 -0
  14. package/dist/lib/store/dump/dump.types.d.ts.map +1 -1
  15. package/dist/lib/store/dump/dumpIndexHelper.d.ts +13 -0
  16. package/dist/lib/store/dump/dumpIndexHelper.d.ts.map +1 -0
  17. package/dist/lib/store/dump/dumpIndexHelper.js +111 -0
  18. package/dist/lib/store/dump/dumpManager.d.ts.map +1 -1
  19. package/dist/lib/store/dump/dumpManager.js +37 -48
  20. package/dist/lib/store/dump/dumpMetadataHelper.d.ts.map +1 -1
  21. package/dist/lib/store/dump/dumpMetadataHelper.js +2 -1
  22. package/dist/lib/utils/fs/fsUtils.js +1 -1
  23. package/dist/lib/utils/string/stringUtils.d.ts +6 -0
  24. package/dist/lib/utils/string/stringUtils.d.ts.map +1 -1
  25. package/dist/lib/utils/string/stringUtils.js +8 -1
  26. package/package.json +4 -4
  27. package/src/http/routes/diff.ts +47 -0
  28. package/src/http/routes/home.ts +1 -0
  29. package/src/http/routes/index.ts +5 -0
  30. package/src/http/routes/subset.ts +1 -1
  31. package/src/http/views/diffIndex.pug +14 -0
  32. package/src/lib/dataServer.ts +17 -1
  33. package/src/lib/store/diff/diff.types.ts +1 -0
  34. package/src/lib/store/diff/diffManager.ts +1 -0
  35. package/src/lib/store/dump/dump.types.ts +5 -0
  36. package/src/lib/store/dump/dumpIndexHelper.ts +132 -0
  37. package/src/lib/store/dump/dumpManager.ts +53 -58
  38. package/src/lib/store/dump/dumpMetadataHelper.ts +2 -1
  39. package/src/lib/utils/fs/fsUtils.ts +1 -2
  40. package/src/lib/utils/string/stringUtils.ts +8 -0
  41. 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,IAalD,CAAC"}
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"}
@@ -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":"AAmCA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAiCxB,eAAe,MAAM,CAAC"}
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;AAE5B;;;;GAIG;AACH,QAAA,MAAM,UAAU;IACd;;;;;;OAMG;oBACa,qBAAqB,KAAG,gBAAgB;CA6BzD,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"}
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"}
@@ -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;IACnB;;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
+ {"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,WAuEzB,CAAC"}
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"}
@@ -72,4 +72,8 @@ export type DumpMetadata = {
72
72
  current: string;
73
73
  dumps: Dump[];
74
74
  };
75
+ export type IndexEntry = {
76
+ hash: string;
77
+ url: string | null;
78
+ };
75
79
  //# sourceMappingURL=dump.types.d.ts.map
@@ -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,13 @@
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
+ addEntry: (relativeFilePath: string, entry: IndexEntry) => void;
11
+ removeEntry: (relativeFilePath: string) => void;
12
+ };
13
+ //# 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;iCAG/B,MAAM,SAAS,UAAU,KAAG,IAAI;oCAK7B,MAAM,KAAG,IAAI;CAI9C,CAAC"}
@@ -0,0 +1,111 @@
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
+ addEntry: (relativeFilePath, entry) => {
104
+ setIsStale(true);
105
+ index.set(relativeFilePath, entry);
106
+ },
107
+ removeEntry: (relativeFilePath) => {
108
+ setIsStale(true);
109
+ index.delete(relativeFilePath);
110
+ },
111
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"dumpManager.d.ts","sourceRoot":"","sources":["../../../../src/lib/store/dump/dumpManager.ts"],"names":[],"mappings":"AAcA,OAAO,EAAQ,WAAW,EAAE,MAAM,cAAc,CAAC;AA2GjD,eAAO,MAAM,WAAW,EAAE,WA8EzB,CAAC"}
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;AA6GjD,eAAO,MAAM,WAAW,EAAE,WA4EzB,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 storeUpdatedFiles = (diff, dump, dumpDir) => {
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 = `${dumpDir}/${relativeFilepath}`;
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
- if (fs_1.default.existsSync(absoluteFilepathInDumpDir)) {
39
- const dumpFileContentString = (0, fs_2.readFile)(absoluteFilepathInDumpDir);
40
- if (storeFileContentString === dumpFileContentString) {
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 && dumpFileContentString) {
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
- // Save it.
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, dumpDir) => {
76
+ const removeDeletedFiles = (diff, dump, filesDumpDir) => {
87
77
  diff.deleted.forEach(relativeFilepath => {
88
- const absoluteFilepath = `${dumpDir}/${relativeFilepath}`;
78
+ const absoluteFilepath = `${filesDumpDir}/${relativeFilepath}`;
89
79
  try {
90
- if (fs_1.default.existsSync(absoluteFilepath)) {
91
- // Get old public URLs before they change on disk.
92
- const dumpContent = (0, fs_2.getFileContent)(absoluteFilepath);
93
- const oldPublicUrl = dumpContent.json?.data?.content?.url?.path;
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,
@@ -121,12 +113,15 @@ exports.dumpManager = {
121
113
  diff,
122
114
  };
123
115
  if (config_1.config.dumpDir) {
124
- const dumpDir = `${config_1.config.dumpDir}/files`;
116
+ const filesDumpDir = `${config_1.config.dumpDir}/files`;
125
117
  if (diff.updated.size || diff.deleted.size) {
118
+ dumpMetadataHelper_1.dumpMetadataHelper.storeDumpMetadata(dump);
126
119
  // Store updated files.
127
- storeUpdatedFiles(diff, dump, dumpDir);
120
+ storeUpdatedFiles(diff, dump, filesDumpDir);
128
121
  // Remove deleted files.
129
- removeDeletedFiles(diff, dump, dumpDir);
122
+ removeDeletedFiles(diff, dump, filesDumpDir);
123
+ // Save dump index
124
+ dumpIndexHelper_1.dumpIndexHelper.saveDumpIndex();
130
125
  // Invoke "onDumpCreate" hook.
131
126
  dump = hook_1.hookManager.invokeOnDumpCreate(dump);
132
127
  const execTimeMs = (microtime_1.default.now() - startDate) / 1000;
@@ -140,12 +135,6 @@ exports.dumpManager = {
140
135
  diffManager_1.diffManager.reset(diff.toUniqueId);
141
136
  }
142
137
  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
138
  }
150
139
  else {
151
140
  // 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;CAWjC,CAAC"}
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"}
@@ -17,7 +17,7 @@ exports.dumpMetadataHelper = {
17
17
  current: workDir_1.unixEpochUniqueId,
18
18
  dumps: [],
19
19
  };
20
- let currentDumpMetadata = fallbackDumpMetadata;
20
+ let currentDumpMetadata;
21
21
  try {
22
22
  currentDumpMetadata = JSON.parse(fs_1.default.readFileSync(metadataFilepath).toString());
23
23
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -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
  : '[]');
@@ -28,7 +28,7 @@ exports.isJsonFile = isJsonFile;
28
28
  * @returns The file contents as a string if file is found, or null otherwise.
29
29
  */
30
30
  const readFile = (filePath) => {
31
- let content = null;
31
+ let content;
32
32
  try {
33
33
  content = fs_1.default.readFileSync(filePath, { encoding: 'utf8', flag: 'r' });
34
34
  }
@@ -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",
3
+ "version": "1.0.5",
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",
@@ -21,7 +21,7 @@
21
21
  "decache": "^4.6.1",
22
22
  "express": "^5.1.0",
23
23
  "fast-glob": "^3.2.12",
24
- "marked": "^17.0.1",
24
+ "marked": "^18.0.3",
25
25
  "microtime": "^3.1.1",
26
26
  "mime-types": "^3.0.1",
27
27
  "pug": "^3.0.3",
@@ -48,7 +48,7 @@
48
48
  "prepare": "husky"
49
49
  },
50
50
  "devDependencies": {
51
- "@eslint/js": "^9.35.0",
51
+ "@eslint/js": "^10.0.1",
52
52
  "@types/cors": "^2.8.13",
53
53
  "@types/express": "^5.0.3",
54
54
  "@types/jest": "^30.0.0",
@@ -59,7 +59,7 @@
59
59
  "@types/yargs": "^17.0.22",
60
60
  "@typescript-eslint/eslint-plugin": "^8.43.0",
61
61
  "@typescript-eslint/parser": "^8.43.0",
62
- "eslint": "^9.35.0",
62
+ "eslint": "^10.3.0",
63
63
  "eslint-import-resolver-typescript": "^4.4.4",
64
64
  "eslint-plugin-import": "^2.27.5",
65
65
  "eslint-plugin-prettier": "^5.5.4",
@@ -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 };
@@ -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',
@@ -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
@@ -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,
@@ -38,6 +38,7 @@ export type Diff = {
38
38
  * Execution time taken by the diff, in milliseconds.
39
39
  */
40
40
  execTimeMs: number;
41
+
41
42
  /**
42
43
  * A unique id representing the date from which this diff is obtained.
43
44
  */
@@ -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;
@@ -76,3 +76,8 @@ export type DumpMetadata = {
76
76
  current: string;
77
77
  dumps: Dump[];
78
78
  };
79
+
80
+ export type IndexEntry = {
81
+ hash: string;
82
+ url: string | null;
83
+ };
@@ -0,0 +1,132 @@
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
+ addEntry: (relativeFilePath: string, entry: IndexEntry): void => {
124
+ setIsStale(true);
125
+ index.set(relativeFilePath, entry);
126
+ },
127
+
128
+ removeEntry: (relativeFilePath: string): void => {
129
+ setIsStale(true);
130
+ index.delete(relativeFilePath);
131
+ },
132
+ };
@@ -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
- const storeUpdatedFiles = (diff: Diff, dump: Dump, dumpDir: string): void => {
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 = `${dumpDir}/${relativeFilepath}`;
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
- if (fs.existsSync(absoluteFilepathInDumpDir)) {
44
- const dumpFileContentString = readFile(absoluteFilepathInDumpDir);
45
- if (storeFileContentString === dumpFileContentString) {
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 && dumpFileContentString) {
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
- // Save it.
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 = (diff: Diff, dump: Dump, dumpDir: string): void => {
85
+ const removeDeletedFiles = (
86
+ diff: Diff,
87
+ dump: Dump,
88
+ filesDumpDir: string,
89
+ ): void => {
95
90
  diff.deleted.forEach(relativeFilepath => {
96
- const absoluteFilepath = `${dumpDir}/${relativeFilepath}`;
91
+ const absoluteFilepath = `${filesDumpDir}/${relativeFilepath}`;
97
92
  try {
98
- if (fs.existsSync(absoluteFilepath)) {
99
- // Get old public URLs before they change on disk.
100
- const dumpContent = getFileContent(absoluteFilepath);
101
- const oldPublicUrl: string | null =
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,
@@ -135,14 +132,19 @@ export const dumpManager: DumpManager = {
135
132
  };
136
133
 
137
134
  if (config.dumpDir) {
138
- const dumpDir = `${config.dumpDir}/files`;
135
+ const filesDumpDir = `${config.dumpDir}/files`;
139
136
 
140
137
  if (diff.updated.size || diff.deleted.size) {
138
+ dumpMetadataHelper.storeDumpMetadata(dump);
139
+
141
140
  // Store updated files.
142
- storeUpdatedFiles(diff, dump, dumpDir);
141
+ storeUpdatedFiles(diff, dump, filesDumpDir);
143
142
 
144
143
  // Remove deleted files.
145
- removeDeletedFiles(diff, dump, dumpDir);
144
+ removeDeletedFiles(diff, dump, filesDumpDir);
145
+
146
+ // Save dump index
147
+ dumpIndexHelper.saveDumpIndex();
146
148
 
147
149
  // Invoke "onDumpCreate" hook.
148
150
  dump = hookManager.invokeOnDumpCreate(dump);
@@ -162,13 +164,6 @@ export const dumpManager: DumpManager = {
162
164
  logger.info(
163
165
  `Dump created in ${execTimeMs} ms. Updated: ${dump.updated.size} / Deleted: ${dump.deleted.size}`,
164
166
  );
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
167
  } else {
173
168
  // Resetting the diff must happen when no other operations are pending.
174
169
  diffManager.reset(diff.toUniqueId);
@@ -14,7 +14,7 @@ export const dumpMetadataHelper = {
14
14
  current: unixEpochUniqueId,
15
15
  dumps: [],
16
16
  };
17
- let currentDumpMetadata = fallbackDumpMetadata;
17
+ let currentDumpMetadata;
18
18
  try {
19
19
  currentDumpMetadata = JSON.parse(
20
20
  fs.readFileSync(metadataFilepath).toString(),
@@ -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()
@@ -25,7 +25,7 @@ export const isJsonFile = (filepath: string): boolean =>
25
25
  * @returns The file contents as a string if file is found, or null otherwise.
26
26
  */
27
27
  export const readFile = (filePath: string): string | null => {
28
- let content = null;
28
+ let content;
29
29
  try {
30
30
  content = fs.readFileSync(filePath, { encoding: 'utf8', flag: 'r' });
31
31
  } catch (error) {
@@ -115,4 +115,3 @@ export const removeEmptyDirsUpwards = (dir: string): void => {
115
115
  removeEmptyDirsUpwards(path.dirname(dir));
116
116
  }
117
117
  };
118
-
@@ -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": [