@epam/uui-db 5.3.0 → 5.4.0

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 (103) hide show
  1. package/package.json +5 -5
  2. package/{build/stats.html → stats.html} +1 -1
  3. package/benchmark/results/filter.json +0 -39
  4. package/benchmark/results/pkLookup.json +0 -39
  5. package/benchmark/results/pkUpdate-10.json +0 -51
  6. package/benchmark/results/pkUpdate-100.json +0 -51
  7. package/benchmark/results/pkUpdate-1000.json +0 -51
  8. package/benchmark/results/pkUpdate-10000.json +0 -51
  9. package/benchmark/results/pkUpdate-100000.json +0 -51
  10. package/benchmark/results/pkUpdate-1000000.json +0 -51
  11. package/benchmark/results/query-10.json +0 -63
  12. package/benchmark/results/query-100.json +0 -63
  13. package/benchmark/results/query-1000.json +0 -63
  14. package/benchmark/results/query-10000.json +0 -63
  15. package/benchmark/results/query-100000.json +0 -63
  16. package/benchmark/results/sorting.json +0 -33
  17. package/build/package.json +0 -69
  18. package/build/readme.md +0 -17
  19. package/globals.d.ts +0 -9
  20. package/index.tsx +0 -1
  21. package/src/Db.ts +0 -89
  22. package/src/DbRef.ts +0 -254
  23. package/src/DbTable.ts +0 -264
  24. package/src/IxSet.ts +0 -251
  25. package/src/ListLoadingTracker.ts +0 -131
  26. package/src/Loader.ts +0 -96
  27. package/src/Scheduler.ts +0 -72
  28. package/src/SimpleLoadingTracker.ts +0 -17
  29. package/src/__benchmarks__/filter.ts +0 -50
  30. package/src/__benchmarks__/index.ts +0 -5
  31. package/src/__benchmarks__/pkLookup.ts +0 -33
  32. package/src/__benchmarks__/pkUpdate.ts +0 -75
  33. package/src/__benchmarks__/query.ts +0 -101
  34. package/src/__benchmarks__/sorting.ts +0 -33
  35. package/src/__benchmarks__/testData.ts +0 -15
  36. package/src/__tests__/ListLoadingTracker.test.ts +0 -83
  37. package/src/__tests__/Scheduler.test.ts +0 -24
  38. package/src/__tests__/TaskDb.ts +0 -187
  39. package/src/__tests__/db.test.ts +0 -293
  40. package/src/__tests__/dbRef.test.ts +0 -100
  41. package/src/__tests__/dbTable.test.ts +0 -15
  42. package/src/__tests__/dbViews.test.ts +0 -76
  43. package/src/__tests__/ixSet.perf.test.ts +0 -129
  44. package/src/__tests__/ixSet.test.ts +0 -73
  45. package/src/__tests__/listLoader.test.ts +0 -85
  46. package/src/__tests__/loader.test.ts +0 -92
  47. package/src/__tests__/patchHelpers.test.ts +0 -202
  48. package/src/__tests__/tempIds.test.ts +0 -98
  49. package/src/index.ts +0 -8
  50. package/src/patchHelpers.ts +0 -254
  51. package/src/reactHooks.ts +0 -28
  52. package/src/tempIds.tsx +0 -236
  53. package/src/types.ts +0 -126
  54. package/src/utils.ts +0 -34
  55. package/tsconfig.json +0 -12
  56. /package/{build/index.d.ts → index.d.ts} +0 -0
  57. /package/{build/index.d.ts.map → index.d.ts.map} +0 -0
  58. /package/{build/index.esm.js → index.esm.js} +0 -0
  59. /package/{build/index.esm.js.map → index.esm.js.map} +0 -0
  60. /package/{build/index.js → index.js} +0 -0
  61. /package/{build/index.js.map → index.js.map} +0 -0
  62. /package/{build/src → src}/Db.d.ts +0 -0
  63. /package/{build/src → src}/Db.d.ts.map +0 -0
  64. /package/{build/src → src}/DbRef.d.ts +0 -0
  65. /package/{build/src → src}/DbRef.d.ts.map +0 -0
  66. /package/{build/src → src}/DbTable.d.ts +0 -0
  67. /package/{build/src → src}/DbTable.d.ts.map +0 -0
  68. /package/{build/src → src}/IxSet.d.ts +0 -0
  69. /package/{build/src → src}/IxSet.d.ts.map +0 -0
  70. /package/{build/src → src}/ListLoadingTracker.d.ts +0 -0
  71. /package/{build/src → src}/ListLoadingTracker.d.ts.map +0 -0
  72. /package/{build/src → src}/Loader.d.ts +0 -0
  73. /package/{build/src → src}/Loader.d.ts.map +0 -0
  74. /package/{build/src → src}/Scheduler.d.ts +0 -0
  75. /package/{build/src → src}/Scheduler.d.ts.map +0 -0
  76. /package/{build/src → src}/SimpleLoadingTracker.d.ts +0 -0
  77. /package/{build/src → src}/SimpleLoadingTracker.d.ts.map +0 -0
  78. /package/{build/src → src}/__benchmarks__/filter.d.ts +0 -0
  79. /package/{build/src → src}/__benchmarks__/filter.d.ts.map +0 -0
  80. /package/{build/src → src}/__benchmarks__/index.d.ts +0 -0
  81. /package/{build/src → src}/__benchmarks__/index.d.ts.map +0 -0
  82. /package/{build/src → src}/__benchmarks__/pkLookup.d.ts +0 -0
  83. /package/{build/src → src}/__benchmarks__/pkLookup.d.ts.map +0 -0
  84. /package/{build/src → src}/__benchmarks__/pkUpdate.d.ts +0 -0
  85. /package/{build/src → src}/__benchmarks__/pkUpdate.d.ts.map +0 -0
  86. /package/{build/src → src}/__benchmarks__/query.d.ts +0 -0
  87. /package/{build/src → src}/__benchmarks__/query.d.ts.map +0 -0
  88. /package/{build/src → src}/__benchmarks__/sorting.d.ts +0 -0
  89. /package/{build/src → src}/__benchmarks__/sorting.d.ts.map +0 -0
  90. /package/{build/src → src}/__benchmarks__/testData.d.ts +0 -0
  91. /package/{build/src → src}/__benchmarks__/testData.d.ts.map +0 -0
  92. /package/{build/src → src}/index.d.ts +0 -0
  93. /package/{build/src → src}/index.d.ts.map +0 -0
  94. /package/{build/src → src}/patchHelpers.d.ts +0 -0
  95. /package/{build/src → src}/patchHelpers.d.ts.map +0 -0
  96. /package/{build/src → src}/reactHooks.d.ts +0 -0
  97. /package/{build/src → src}/reactHooks.d.ts.map +0 -0
  98. /package/{build/src → src}/tempIds.d.ts +0 -0
  99. /package/{build/src → src}/tempIds.d.ts.map +0 -0
  100. /package/{build/src → src}/types.d.ts +0 -0
  101. /package/{build/src → src}/types.d.ts.map +0 -0
  102. /package/{build/src → src}/utils.d.ts +0 -0
  103. /package/{build/src → src}/utils.d.ts.map +0 -0
@@ -1,63 +0,0 @@
1
- {
2
- "name": "Find N entities in 1000 dataset by departmentId, sorted by name",
3
- "date": "2020-06-10T00:29:36.431Z",
4
- "version": "1.0.0",
5
- "results": [
6
- {
7
- "name": "Array - scan",
8
- "ops": 278416,
9
- "margin": 2.35,
10
- "percentSlower": 87.88
11
- },
12
- {
13
- "name": "Array - with filterPredicate/orderComparer",
14
- "ops": 68167,
15
- "margin": 3.37,
16
- "percentSlower": 97.03
17
- },
18
- {
19
- "name": "BTree - toArray/scan",
20
- "ops": 23840,
21
- "margin": 2.65,
22
- "percentSlower": 98.96
23
- },
24
- {
25
- "name": "I.Map - iterable.filter.sort, then toArray",
26
- "ops": 22083,
27
- "margin": 1.73,
28
- "percentSlower": 99.04
29
- },
30
- {
31
- "name": "I.Map - toArray, then filter/sort",
32
- "ops": 17308,
33
- "margin": 1.21,
34
- "percentSlower": 99.25
35
- },
36
- {
37
- "name": "IxSet - with indexes",
38
- "ops": 184695,
39
- "margin": 1.28,
40
- "percentSlower": 91.96
41
- },
42
- {
43
- "name": "IxSet - no indexes",
44
- "ops": 21450,
45
- "margin": 1.04,
46
- "percentSlower": 99.07
47
- },
48
- {
49
- "name": "DbTable - with index",
50
- "ops": 2298027,
51
- "margin": 1.28,
52
- "percentSlower": 0
53
- }
54
- ],
55
- "fastest": {
56
- "name": "DbTable - with index",
57
- "index": 7
58
- },
59
- "slowest": {
60
- "name": "I.Map - toArray, then filter/sort",
61
- "index": 4
62
- }
63
- }
@@ -1,63 +0,0 @@
1
- {
2
- "name": "Find N entities in 10000 dataset by departmentId, sorted by name",
3
- "date": "2020-06-10T00:30:19.909Z",
4
- "version": "1.0.0",
5
- "results": [
6
- {
7
- "name": "Array - scan",
8
- "ops": 27704,
9
- "margin": 3.13,
10
- "percentSlower": 98.71
11
- },
12
- {
13
- "name": "Array - with filterPredicate/orderComparer",
14
- "ops": 6806,
15
- "margin": 3.02,
16
- "percentSlower": 99.68
17
- },
18
- {
19
- "name": "BTree - toArray/scan",
20
- "ops": 2544,
21
- "margin": 1.27,
22
- "percentSlower": 99.88
23
- },
24
- {
25
- "name": "I.Map - iterable.filter.sort, then toArray",
26
- "ops": 2458,
27
- "margin": 1.39,
28
- "percentSlower": 99.89
29
- },
30
- {
31
- "name": "I.Map - toArray, then filter/sort",
32
- "ops": 1783,
33
- "margin": 1.51,
34
- "percentSlower": 99.92
35
- },
36
- {
37
- "name": "IxSet - with indexes",
38
- "ops": 118744,
39
- "margin": 1.19,
40
- "percentSlower": 94.49
41
- },
42
- {
43
- "name": "IxSet - no indexes",
44
- "ops": 2063,
45
- "margin": 3.59,
46
- "percentSlower": 99.9
47
- },
48
- {
49
- "name": "DbTable - with index",
50
- "ops": 2154284,
51
- "margin": 1.22,
52
- "percentSlower": 0
53
- }
54
- ],
55
- "fastest": {
56
- "name": "DbTable - with index",
57
- "index": 7
58
- },
59
- "slowest": {
60
- "name": "I.Map - toArray, then filter/sort",
61
- "index": 4
62
- }
63
- }
@@ -1,63 +0,0 @@
1
- {
2
- "name": "Find N entities in 100000 dataset by departmentId, sorted by name",
3
- "date": "2020-06-10T00:31:03.956Z",
4
- "version": "1.0.0",
5
- "results": [
6
- {
7
- "name": "Array - scan",
8
- "ops": 1830,
9
- "margin": 4.74,
10
- "percentSlower": 99.91
11
- },
12
- {
13
- "name": "Array - with filterPredicate/orderComparer",
14
- "ops": 668,
15
- "margin": 3.19,
16
- "percentSlower": 99.97
17
- },
18
- {
19
- "name": "BTree - toArray/scan",
20
- "ops": 183,
21
- "margin": 5.36,
22
- "percentSlower": 99.99
23
- },
24
- {
25
- "name": "I.Map - iterable.filter.sort, then toArray",
26
- "ops": 50,
27
- "margin": 2.33,
28
- "percentSlower": 100
29
- },
30
- {
31
- "name": "I.Map - toArray, then filter/sort",
32
- "ops": 36,
33
- "margin": 14.94,
34
- "percentSlower": 100
35
- },
36
- {
37
- "name": "IxSet - with indexes",
38
- "ops": 76943,
39
- "margin": 7.77,
40
- "percentSlower": 96.16
41
- },
42
- {
43
- "name": "IxSet - no indexes",
44
- "ops": 135,
45
- "margin": 8.63,
46
- "percentSlower": 99.99
47
- },
48
- {
49
- "name": "DbTable - with index",
50
- "ops": 2003311,
51
- "margin": 4.35,
52
- "percentSlower": 0
53
- }
54
- ],
55
- "fastest": {
56
- "name": "DbTable - with index",
57
- "index": 7
58
- },
59
- "slowest": {
60
- "name": "I.Map - toArray, then filter/sort",
61
- "index": 4
62
- }
63
- }
@@ -1,33 +0,0 @@
1
- {
2
- "name": "Sort array",
3
- "date": "2020-04-27T23:07:24.758Z",
4
- "version": "1.0.0",
5
- "results": [
6
- {
7
- "name": "array.sort, hard-coded",
8
- "ops": 2645,
9
- "margin": 2.18,
10
- "percentSlower": 0
11
- },
12
- {
13
- "name": "_.sort, hard-coded",
14
- "ops": 464,
15
- "margin": 1.96,
16
- "percentSlower": 82.46
17
- },
18
- {
19
- "name": "array.sort, getOrderComparer",
20
- "ops": 1939,
21
- "margin": 0.47,
22
- "percentSlower": 26.69
23
- }
24
- ],
25
- "fastest": {
26
- "name": "array.sort, hard-coded",
27
- "index": 0
28
- },
29
- "slowest": {
30
- "name": "_.sort, hard-coded",
31
- "index": 1
32
- }
33
- }
@@ -1,69 +0,0 @@
1
- {
2
- "name": "@epam/uui-db",
3
- "version": "5.3.0",
4
- "description": "UUI - client-side relational state cache",
5
- "author": "EPAM",
6
- "license": "MIT",
7
- "module": "index.esm.js",
8
- "main": "index.js",
9
- "typings": "index.d.ts",
10
- "scripts": {
11
- "build": "node ../uui-build/scripts/buildModule",
12
- "perf": "npx ts-node -T ./src/__benchmarks__/index.ts",
13
- "perf-prof": "node -r ts-node/register/transpile-only --prof ./src/__benchmarks__/index.ts",
14
- "prepublish": "yarn build"
15
- },
16
- "dependencies": {
17
- "@epam/uui-core": "5.3.0",
18
- "@types/lodash.countby": "4.6.6",
19
- "@types/lodash.filter": "4.6.6",
20
- "@types/lodash.foreach": "4.5.6",
21
- "@types/lodash.groupby": "4.6.6",
22
- "@types/lodash.isempty": "4.4.6",
23
- "@types/lodash.isequal": "4.5.5",
24
- "@types/lodash.map": "4.6.13",
25
- "@types/lodash.merge": "4.6.6",
26
- "@types/lodash.orderby": "4.6.6",
27
- "@types/lodash.pick": "4.4.6",
28
- "@types/lodash.range": "3.2.6",
29
- "@types/lodash.sortby": "4.7.6",
30
- "@types/lodash.transform": "4.6.6",
31
- "@types/lodash.zip": "4.2.6",
32
- "@types/shallowequal": "^1.1.1",
33
- "immutable": "3.8.2",
34
- "lodash.countby": "4.6.0",
35
- "lodash.filter": "4.6.0",
36
- "lodash.foreach": "4.5.0",
37
- "lodash.groupby": "4.6.0",
38
- "lodash.isempty": "4.4.0",
39
- "lodash.isequal": "4.5.0",
40
- "lodash.map": "4.6.0",
41
- "lodash.merge": "4.6.2",
42
- "lodash.orderby": "4.6.0",
43
- "lodash.pick": "4.4.0",
44
- "lodash.range": "3.2.0",
45
- "lodash.sortby": "4.7.0",
46
- "lodash.transform": "4.6.0",
47
- "lodash.zip": "4.2.0",
48
- "shallowequal": "^1.1.0",
49
- "sorted-btree": "^1.2.1"
50
- },
51
- "devDependencies": {
52
- "@types/benchmark": "^1.0.31",
53
- "benny": "^3.6.14",
54
- "ts-node": "10.9.1"
55
- },
56
- "peerDependencies": {
57
- "prop-types": "*",
58
- "react": ">=16.0.0",
59
- "react-dom": ">=16.0.0"
60
- },
61
- "repository": {
62
- "type": "git",
63
- "url": "https://github.com/epam/UUI"
64
- },
65
- "gitHead": "e5bbda766f5bdfd7c42a3c62c34afd9c03fffedd",
66
- "publishConfig": {
67
- "directory": "build"
68
- }
69
- }
package/build/readme.md DELETED
@@ -1,17 +0,0 @@
1
- # UUI DB package
2
-
3
- This package is a part of [EPAM UUI](https://github.com/epam/UUI) library.
4
-
5
- @epam/uui-db is an experimental state-management solution. It maintains immutable relational database on client, provides means to load data from server, update it both from client, and save it to server asynchronously with UI operations.
6
-
7
- ## Features
8
-
9
- - relation data model for state, providing a normalized storage for UI state
10
- - data loading from server, with automatic GraphQL response normalization into tables
11
- - synchronous and transactional updates of state from UI
12
- - asynchronous bi-directional state synchronization between UI and server. UI changes in terms of 'patches' are batched and sent to server. Server can also push updates to UI state
13
- - GIT-like branch-rebase approach to handle state concurrency
14
- - tables can be indexed to speed-up queries
15
- - Views to build arbitrary state projections
16
- - subscriptions to subscribe parts of UI to changes in Views
17
-
package/globals.d.ts DELETED
@@ -1,9 +0,0 @@
1
- /// <reference path="../uui_globals.d.ts" />
2
-
3
- declare module 'jsurl';
4
- declare module 'query-string';
5
- declare module 'draft-js-plugins-editor';
6
- declare module 'draft-js-clear-formatting';
7
- declare module 'draft-convert';
8
- declare module 'draft-js-plugins-utils';
9
- declare module 'draft-js/lib/*';
package/index.tsx DELETED
@@ -1 +0,0 @@
1
- export * from './src';
package/src/Db.ts DELETED
@@ -1,89 +0,0 @@
1
- import {
2
- DbPatch, DbTablesSet, DbTableEntityByTable, DbView, DbRelationType, ViewCacheItem,
3
- } from './types';
4
- import { objectKeys, defaultCompareViewDependencies, difference } from './utils';
5
- import isEqual from 'lodash.isequal';
6
-
7
- type ViewCache = Map<DbView<any, any, any, any>, Map<string, ViewCacheItem>>;
8
-
9
- export abstract class Db<TTables extends DbTablesSet<TTables>> {
10
- private viewCache: ViewCache = new Map();
11
- constructor(public tables: TTables, reusedCache?: ViewCache) {
12
- if (reusedCache) {
13
- this.viewCache = new Map(reusedCache);
14
- } else {
15
- this.viewCache = new Map();
16
- }
17
- }
18
-
19
- private update(mutate: (db: this) => void): this {
20
- const clone = new (this.constructor as any)(this.tables, this.viewCache);
21
- clone.tables = { ...this.tables };
22
- mutate(clone);
23
- return clone;
24
- }
25
-
26
- public with(patch: DbPatch<TTables>): this {
27
- return this.update((db) => {
28
- objectKeys(db.tables).forEach((entityName) => {
29
- if (patch[entityName]) {
30
- db.tables[entityName] = db.tables[entityName].with(patch[entityName]);
31
- }
32
- });
33
- });
34
- }
35
-
36
- /* Views (cached db projections) */
37
-
38
- valueAbsent = Symbol('ValueAbsent');
39
- public runView<TResult, TParams = void, TDependencies = void>(view: DbView<this, TResult, TParams, TDependencies>, params?: TParams) {
40
- let cache = this.viewCache.get(view);
41
-
42
- if (!cache) {
43
- cache = new Map();
44
- this.viewCache.set(view, cache);
45
- }
46
-
47
- const key = view.getKey ? view.getKey(params) : JSON.stringify(params);
48
-
49
- let cacheItem: ViewCacheItem = cache.get(key);
50
-
51
- if (!cacheItem) {
52
- cacheItem = {
53
- dependencies: this.valueAbsent,
54
- currentValue: this.valueAbsent,
55
- computedOnDb: this.valueAbsent,
56
- };
57
- cache.set(key, cacheItem);
58
- }
59
-
60
- let shouldUpdate = this !== cacheItem.computedOnDb;
61
- let dependencies: any = this.valueAbsent;
62
-
63
- if (view.getDependencies) {
64
- const compare = view.compareDependencies || defaultCompareViewDependencies;
65
- dependencies = view.getDependencies(this, params);
66
- shouldUpdate = !compare(cacheItem.dependencies, dependencies);
67
- }
68
-
69
- const previousValue = cacheItem.currentValue;
70
-
71
- if (shouldUpdate) {
72
- cacheItem.dependencies = dependencies;
73
- const newValue = view.compute(this, params, dependencies);
74
- cacheItem.computedOnDb = this;
75
-
76
- const compareResults = view.compareResults || isEqual;
77
-
78
- if (!compareResults(previousValue, newValue)) {
79
- if (view.traceDiff) {
80
- console.debug(difference(previousValue, newValue));
81
- }
82
-
83
- cacheItem.currentValue = newValue;
84
- }
85
- }
86
-
87
- return cacheItem.currentValue as TResult;
88
- }
89
- }
package/src/DbRef.ts DELETED
@@ -1,254 +0,0 @@
1
- import { Db } from './Db';
2
- import {
3
- DbPatch, DbTablesSet, ServerError, DbSaveResponse, DbView, ViewCacheItem, DbSubscription, DbPkFieldType,
4
- } from './types';
5
- import {
6
- makeCumulativePatch, unionPatches, mergeEntityPatches, flattenResponse,
7
- } from './patchHelpers';
8
- import { TempIdMap, IClientIdsMap } from './tempIds';
9
- import { objectKeys, defaultCompareViewDependencies, difference } from './utils';
10
- import isEmpty from 'lodash.isempty';
11
- import { Loader, LoaderOptions } from './Loader';
12
- import { DataQuery, LazyDataSourceApiResponse, batch } from '@epam/uui-core';
13
- import { SimpleLoadingTracker } from './SimpleLoadingTracker';
14
- import { ListLoadingTracker, ListLoadingTrackerOptions } from './ListLoadingTracker';
15
-
16
- export class DbRef<TTables extends DbTablesSet<TTables>, TDb extends Db<TTables>> {
17
- private base: TDb;
18
- private log: { patch: DbPatch<TTables> }[] = [];
19
- private autoSave = true;
20
- private savedPoint = 0;
21
- private tempIdMap: TempIdMap<TTables>;
22
- public db: TDb;
23
- protected throttleSaveMs = 1000;
24
- public readonly idMap: IClientIdsMap;
25
- constructor(public blank: TDb) {
26
- this.db = blank;
27
- this.base = blank;
28
- this.tempIdMap = new TempIdMap(blank);
29
- this.idMap = this.tempIdMap;
30
-
31
- window.addEventListener('beforeunload', this.handleBeforeUnload);
32
- }
33
-
34
- /** Saves all committed patches. Useful only if autoSave is off. */
35
- public save() {
36
- return this.enqueueSave();
37
- }
38
-
39
- public getAutoSave() {
40
- return this.autoSave;
41
- }
42
-
43
- public setAutoSave(val: boolean) {
44
- if (this.autoSave != val) {
45
- this.autoSave = val;
46
- this.autoSave && this.enqueueSave();
47
- this.update();
48
- }
49
- }
50
-
51
- public revert() {
52
- this.db = this.base;
53
- this.log = this.log.slice(0, this.savedPoint);
54
- this.update();
55
- }
56
-
57
- /** Concrete DbRef instances should override and implement their save logic in this method */
58
- protected savePatch(patch: DbPatch<TTables>): Promise<DbSaveResponse<TTables>> {
59
- return Promise.resolve({ submit: {} });
60
- }
61
-
62
- /* Db Update logic */
63
-
64
- public commit(patch: DbPatch<TTables>): void {
65
- const dbBeforeUpdate = this.db.with(patch);
66
- patch = this.beforeUpdate(patch, dbBeforeUpdate.tables, this.db.tables);
67
- patch = mergeEntityPatches(this.db.tables, patch);
68
- this.log.push({ patch });
69
- this.db = this.db.with(patch);
70
-
71
- this.autoSave && this.enqueueSave(null, { throttleMs: this.throttleSaveMs });
72
- this.update();
73
- }
74
-
75
- private beforeUpdate(patch: DbPatch<TTables>, tables: TTables, prevTables: TTables): DbPatch<TTables> {
76
- let patchAndDependencies: DbPatch<TTables> = {};
77
-
78
- objectKeys(tables)
79
- .filter((entityName) => patch[entityName] && !tables[entityName].schema.beforeUpdate)
80
- .forEach((entityName) => {
81
- patchAndDependencies[entityName] = patch[entityName];
82
- });
83
-
84
- objectKeys(tables)
85
- .filter((entityName) => patch[entityName] && tables[entityName].schema.beforeUpdate)
86
- .forEach((entityName) => {
87
- const schema = tables[entityName].schema;
88
- const { beforeUpdate } = schema;
89
- const context = {
90
- clientIdsMap: this.tempIdMap,
91
- schema,
92
- tables,
93
- prevTables,
94
- };
95
-
96
- const results = patch[entityName].map((e) => beforeUpdate(e, context));
97
- const updatedPatch = { [entityName]: results.map((result) => result.entity) } as DbPatch<TTables>;
98
- const dependentEntityPatchesForUpdate = unionPatches(results.filter((result) => result.dependentEntities).map((result) => result.dependentEntities));
99
- const dependentEntityPatches = this.beforeUpdate(dependentEntityPatchesForUpdate, tables, prevTables);
100
-
101
- patchAndDependencies = mergeEntityPatches(tables, unionPatches([
102
- patchAndDependencies, updatedPatch, dependentEntityPatches,
103
- ]));
104
- });
105
- return patchAndDependencies;
106
- }
107
-
108
- public commitFetch(patch: DbPatch<TTables>): void {
109
- patch = this.tempIdMap.serverToClientPatch(patch);
110
- this.rebase(this.base.with(patch));
111
- this.update();
112
- }
113
-
114
- private rebase(newBase: TDb) {
115
- this.base = newBase;
116
- this.db = newBase;
117
- for (let n = this.savedPoint; n < this.log.length; n++) {
118
- this.db = this.db.with(this.log[n].patch);
119
- }
120
- }
121
-
122
- private makeCumulativePatch() {
123
- const lastLogEntry = this.log.length;
124
- const logEntriesToSave = this.log.slice(this.savedPoint, lastLogEntry);
125
- let cumulativePatch = makeCumulativePatch(
126
- this.db,
127
- logEntriesToSave.map((t) => t.patch),
128
- this.tempIdMap,
129
- );
130
- cumulativePatch = this.tempIdMap.clientToServerPatch(cumulativePatch);
131
-
132
- return cumulativePatch;
133
- }
134
-
135
- private applyResponse(response: DbSaveResponse<TTables>, newSavedPoint: number) {
136
- this.tempIdMap.appendServerMapping(response);
137
-
138
- let serverPatch = flattenResponse(response.submit, this.db.tables);
139
- serverPatch = mergeEntityPatches(this.db.tables, serverPatch);
140
- this.savedPoint = newSavedPoint;
141
- this.base = this.db;
142
- this.commitFetch(serverPatch);
143
- }
144
-
145
- /* Update subscriptions (aka live views) */
146
-
147
- private lastSubscriptionId = 1;
148
- private subscriptions: Map<number, DbSubscription<any, any>> = new Map();
149
- public subscribe<TValue, TParams, TDependencies>(
150
- view: DbView<TDb, TValue, TParams, TDependencies>,
151
- params: TParams,
152
- onUpdate: (newValue: TValue) => any,
153
- ): DbSubscription<TValue, TParams> {
154
- const id = this.lastSubscriptionId++;
155
-
156
- const subscription: DbSubscription<TValue, TParams> = {
157
- update: (subscriptionParams: TParams) => {
158
- subscription.currentParams = subscriptionParams;
159
- subscription.currentValue = this.db.runView(view, subscriptionParams);
160
- return subscription.currentValue;
161
- },
162
- currentParams: null,
163
- currentValue: null,
164
- onUpdate,
165
- unsubscribe: () => {
166
- this.subscriptions.delete(id);
167
- },
168
- };
169
-
170
- subscription.update(params);
171
-
172
- this.subscriptions.set(id, subscription);
173
-
174
- return subscription;
175
- }
176
-
177
- updateHandlers: (() => any)[] = [];
178
- public onUpdate(handler: () => any) {
179
- this.subscribe(
180
- {
181
- compute: (db) => db,
182
- compareResults: (a, b) => a === b,
183
- },
184
- null,
185
- handler,
186
- );
187
- }
188
-
189
- private update() {
190
- this.subscriptions.forEach((subscription) => {
191
- const previousValue = subscription.currentValue;
192
- subscription.update(subscription.currentParams);
193
- if (subscription.currentValue !== previousValue && subscription.onUpdate) {
194
- subscription.onUpdate(subscription.currentValue);
195
- }
196
- });
197
- }
198
-
199
- /* Error Subscriptions */
200
-
201
- public saveErrorHandlers: ((request: DbPatch<TTables>, error: any) => void)[] = [];
202
- public onSaveError(handler: (request: DbPatch<TTables>, error: any) => void) {
203
- this.saveErrorHandlers.push(handler);
204
- }
205
-
206
- private saveError(request: DbPatch<TTables>, error: any) {
207
- this.saveErrorHandlers.forEach((handler) => handler(request, error));
208
- }
209
-
210
- /* Save scheduling */
211
-
212
- private enqueueSave = batch(async () => {
213
- const cumulativePatch = this.makeCumulativePatch();
214
- const lastLogEntry = this.log.length;
215
- if (isEmpty(cumulativePatch)) {
216
- return;
217
- }
218
- try {
219
- const response = await this.savePatch(cumulativePatch);
220
- this.applyResponse(response, lastLogEntry);
221
- this.update();
222
- } catch (error) {
223
- this.saveError(cumulativePatch, error);
224
- this.update();
225
- throw error;
226
- }
227
- });
228
-
229
- private handleBeforeUnload = (e: BeforeUnloadEvent) => {
230
- if (this.enqueueSave.isBusy) {
231
- e.returnValue = false;
232
- return false;
233
- }
234
- };
235
-
236
- loaders: Loader<TTables, any, any>[] = [];
237
- protected makeLoader<TResponse, TRequest>(options: LoaderOptions<TTables, TResponse, TRequest>) {
238
- const loader = new Loader<TTables, TResponse, TRequest>(this as any, () => new SimpleLoadingTracker<TRequest, TResponse>(), options);
239
- this.loaders.push(loader);
240
- return loader;
241
- }
242
-
243
- protected makeListLoader<TItem, TResponse = DataQuery<TItem>, TRequest extends DataQuery<TItem> = DataQuery<TItem>>(
244
- options: LoaderOptions<TTables, TResponse, TRequest> & ListLoadingTrackerOptions<TItem, TResponse>,
245
- ) {
246
- const loader = new Loader<TTables, TResponse, TRequest>(this as any, () => new ListLoadingTracker<TItem, TRequest, TResponse>(), options);
247
- this.loaders.push(loader);
248
- return loader;
249
- }
250
-
251
- reload() {
252
- this.loaders = [];
253
- }
254
- }