@epam/uui-db 4.9.0 → 4.9.2
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/bundle-stats.json +1 -1
- package/index.d.ts +1 -1
- package/index.js +1102 -1102
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/readme.md +17 -17
- package/src/Db.d.ts +12 -12
- package/src/DbRef.d.ts +46 -46
- package/src/DbTable.d.ts +34 -34
- package/src/IxSet.d.ts +27 -27
- package/src/ListLoadingTracker.d.ts +18 -18
- package/src/Loader.d.ts +23 -23
- package/src/Scheduler.d.ts +18 -18
- package/src/SimpleLoadingTracker.d.ts +8 -8
- package/src/__benchmarks__/filter.d.ts +1 -1
- package/src/__benchmarks__/index.d.ts +2 -2
- package/src/__benchmarks__/pkLookup.d.ts +1 -1
- package/src/__benchmarks__/pkUpdate.d.ts +1 -1
- package/src/__benchmarks__/query.d.ts +1 -1
- package/src/__benchmarks__/sorting.d.ts +1 -1
- package/src/__benchmarks__/testData.d.ts +14 -14
- package/src/index.d.ts +8 -8
- package/src/patchHelpers.d.ts +17 -17
- package/src/reactHooks.d.ts +6 -6
- package/src/tempIds.d.ts +27 -27
- package/src/types.d.ts +117 -117
- package/src/utils.d.ts +9 -9
package/index.js
CHANGED
|
@@ -381,10 +381,10 @@ function __classPrivateFieldIn(state, receiver) {
|
|
|
381
381
|
/***/ (function(module, exports, __webpack_require__) {
|
|
382
382
|
|
|
383
383
|
"use strict";
|
|
384
|
-
|
|
385
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
386
|
-
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
387
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./src */ "./src/index.ts"), exports);
|
|
384
|
+
|
|
385
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
386
|
+
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
387
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./src */ "./src/index.ts"), exports);
|
|
388
388
|
|
|
389
389
|
|
|
390
390
|
/***/ }),
|
|
@@ -397,80 +397,80 @@ const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es
|
|
|
397
397
|
/***/ (function(module, exports, __webpack_require__) {
|
|
398
398
|
|
|
399
399
|
"use strict";
|
|
400
|
-
|
|
401
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
402
|
-
exports.Db = void 0;
|
|
403
|
-
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
404
|
-
const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
405
|
-
const lodash_isequal_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.isequal */ "lodash.isequal"));
|
|
406
|
-
class Db {
|
|
407
|
-
constructor(tables, reusedCache) {
|
|
408
|
-
this.tables = tables;
|
|
409
|
-
this.viewCache = new Map();
|
|
410
|
-
/* Views (cached db projections) */
|
|
411
|
-
this.valueAbsent = Symbol("ValueAbsent");
|
|
412
|
-
if (reusedCache) {
|
|
413
|
-
this.viewCache = new Map(reusedCache);
|
|
414
|
-
}
|
|
415
|
-
else {
|
|
416
|
-
this.viewCache = new Map();
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
update(mutate) {
|
|
420
|
-
const clone = new this.constructor(this.tables, this.viewCache);
|
|
421
|
-
clone.tables = Object.assign({}, this.tables);
|
|
422
|
-
mutate(clone);
|
|
423
|
-
return clone;
|
|
424
|
-
}
|
|
425
|
-
with(patch) {
|
|
426
|
-
return this.update(db => {
|
|
427
|
-
(0, utils_1.objectKeys)(db.tables).forEach(entityName => {
|
|
428
|
-
if (patch[entityName]) {
|
|
429
|
-
db.tables[entityName] = db.tables[entityName].with(patch[entityName]);
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
runView(view, params) {
|
|
435
|
-
let cache = this.viewCache.get(view);
|
|
436
|
-
if (!cache) {
|
|
437
|
-
cache = new Map();
|
|
438
|
-
this.viewCache.set(view, cache);
|
|
439
|
-
}
|
|
440
|
-
const key = view.getKey ? view.getKey(params) : JSON.stringify(params);
|
|
441
|
-
let cacheItem = cache.get(key);
|
|
442
|
-
if (!cacheItem) {
|
|
443
|
-
cacheItem = {
|
|
444
|
-
dependencies: this.valueAbsent,
|
|
445
|
-
currentValue: this.valueAbsent,
|
|
446
|
-
computedOnDb: this.valueAbsent,
|
|
447
|
-
};
|
|
448
|
-
cache.set(key, cacheItem);
|
|
449
|
-
}
|
|
450
|
-
let shouldUpdate = this !== cacheItem.computedOnDb;
|
|
451
|
-
let dependencies = this.valueAbsent;
|
|
452
|
-
if (view.getDependencies) {
|
|
453
|
-
const compare = view.compareDependencies || utils_1.defaultCompareViewDependencies;
|
|
454
|
-
dependencies = view.getDependencies(this, params);
|
|
455
|
-
shouldUpdate = !compare(cacheItem.dependencies, dependencies);
|
|
456
|
-
}
|
|
457
|
-
let previousValue = cacheItem.currentValue;
|
|
458
|
-
if (shouldUpdate) {
|
|
459
|
-
cacheItem.dependencies = dependencies;
|
|
460
|
-
let newValue = view.compute(this, params, dependencies);
|
|
461
|
-
cacheItem.computedOnDb = this;
|
|
462
|
-
let compareResults = view.compareResults || lodash_isequal_1.default;
|
|
463
|
-
if (!compareResults(previousValue, newValue)) {
|
|
464
|
-
if (view.traceDiff) {
|
|
465
|
-
console.debug((0, utils_1.difference)(previousValue, newValue));
|
|
466
|
-
}
|
|
467
|
-
cacheItem.currentValue = newValue;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
return cacheItem.currentValue;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
exports.Db = Db;
|
|
400
|
+
|
|
401
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
402
|
+
exports.Db = void 0;
|
|
403
|
+
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
404
|
+
const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
405
|
+
const lodash_isequal_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.isequal */ "lodash.isequal"));
|
|
406
|
+
class Db {
|
|
407
|
+
constructor(tables, reusedCache) {
|
|
408
|
+
this.tables = tables;
|
|
409
|
+
this.viewCache = new Map();
|
|
410
|
+
/* Views (cached db projections) */
|
|
411
|
+
this.valueAbsent = Symbol("ValueAbsent");
|
|
412
|
+
if (reusedCache) {
|
|
413
|
+
this.viewCache = new Map(reusedCache);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
this.viewCache = new Map();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
update(mutate) {
|
|
420
|
+
const clone = new this.constructor(this.tables, this.viewCache);
|
|
421
|
+
clone.tables = Object.assign({}, this.tables);
|
|
422
|
+
mutate(clone);
|
|
423
|
+
return clone;
|
|
424
|
+
}
|
|
425
|
+
with(patch) {
|
|
426
|
+
return this.update(db => {
|
|
427
|
+
(0, utils_1.objectKeys)(db.tables).forEach(entityName => {
|
|
428
|
+
if (patch[entityName]) {
|
|
429
|
+
db.tables[entityName] = db.tables[entityName].with(patch[entityName]);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
runView(view, params) {
|
|
435
|
+
let cache = this.viewCache.get(view);
|
|
436
|
+
if (!cache) {
|
|
437
|
+
cache = new Map();
|
|
438
|
+
this.viewCache.set(view, cache);
|
|
439
|
+
}
|
|
440
|
+
const key = view.getKey ? view.getKey(params) : JSON.stringify(params);
|
|
441
|
+
let cacheItem = cache.get(key);
|
|
442
|
+
if (!cacheItem) {
|
|
443
|
+
cacheItem = {
|
|
444
|
+
dependencies: this.valueAbsent,
|
|
445
|
+
currentValue: this.valueAbsent,
|
|
446
|
+
computedOnDb: this.valueAbsent,
|
|
447
|
+
};
|
|
448
|
+
cache.set(key, cacheItem);
|
|
449
|
+
}
|
|
450
|
+
let shouldUpdate = this !== cacheItem.computedOnDb;
|
|
451
|
+
let dependencies = this.valueAbsent;
|
|
452
|
+
if (view.getDependencies) {
|
|
453
|
+
const compare = view.compareDependencies || utils_1.defaultCompareViewDependencies;
|
|
454
|
+
dependencies = view.getDependencies(this, params);
|
|
455
|
+
shouldUpdate = !compare(cacheItem.dependencies, dependencies);
|
|
456
|
+
}
|
|
457
|
+
let previousValue = cacheItem.currentValue;
|
|
458
|
+
if (shouldUpdate) {
|
|
459
|
+
cacheItem.dependencies = dependencies;
|
|
460
|
+
let newValue = view.compute(this, params, dependencies);
|
|
461
|
+
cacheItem.computedOnDb = this;
|
|
462
|
+
let compareResults = view.compareResults || lodash_isequal_1.default;
|
|
463
|
+
if (!compareResults(previousValue, newValue)) {
|
|
464
|
+
if (view.traceDiff) {
|
|
465
|
+
console.debug((0, utils_1.difference)(previousValue, newValue));
|
|
466
|
+
}
|
|
467
|
+
cacheItem.currentValue = newValue;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return cacheItem.currentValue;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
exports.Db = Db;
|
|
474
474
|
|
|
475
475
|
|
|
476
476
|
/***/ }),
|
|
@@ -483,197 +483,197 @@ exports.Db = Db;
|
|
|
483
483
|
/***/ (function(module, exports, __webpack_require__) {
|
|
484
484
|
|
|
485
485
|
"use strict";
|
|
486
|
-
|
|
487
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
488
|
-
exports.DbRef = void 0;
|
|
489
|
-
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
490
|
-
const patchHelpers_1 = __webpack_require__(/*! ./patchHelpers */ "./src/patchHelpers.ts");
|
|
491
|
-
const tempIds_1 = __webpack_require__(/*! ./tempIds */ "./src/tempIds.tsx");
|
|
492
|
-
const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
493
|
-
const lodash_isempty_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.isempty */ "lodash.isempty"));
|
|
494
|
-
const Loader_1 = __webpack_require__(/*! ./Loader */ "./src/Loader.ts");
|
|
495
|
-
const uui_core_1 = __webpack_require__(/*! @epam/uui-core */ "@epam/uui-core");
|
|
496
|
-
const SimpleLoadingTracker_1 = __webpack_require__(/*! ./SimpleLoadingTracker */ "./src/SimpleLoadingTracker.ts");
|
|
497
|
-
const ListLoadingTracker_1 = __webpack_require__(/*! ./ListLoadingTracker */ "./src/ListLoadingTracker.ts");
|
|
498
|
-
class DbRef {
|
|
499
|
-
constructor(blank) {
|
|
500
|
-
this.blank = blank;
|
|
501
|
-
this.log = [];
|
|
502
|
-
this.autoSave = true;
|
|
503
|
-
this.savedPoint = 0;
|
|
504
|
-
this.throttleSaveMs = 1000;
|
|
505
|
-
/* Update subscriptions (aka live views) */
|
|
506
|
-
this.lastSubscriptionId = 1;
|
|
507
|
-
this.subscriptions = new Map();
|
|
508
|
-
this.updateHandlers = [];
|
|
509
|
-
/* Error Subscriptions */
|
|
510
|
-
this.saveErrorHandlers = [];
|
|
511
|
-
/* Save scheduling */
|
|
512
|
-
this.enqueueSave = (0, uui_core_1.batch)(() => (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
|
|
513
|
-
const cumulativePatch = this.makeCumulativePatch();
|
|
514
|
-
const lastLogEntry = this.log.length;
|
|
515
|
-
if ((0, lodash_isempty_1.default)(cumulativePatch)) {
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
try {
|
|
519
|
-
const response = yield this.savePatch(cumulativePatch);
|
|
520
|
-
this.applyResponse(response, lastLogEntry);
|
|
521
|
-
this.update();
|
|
522
|
-
}
|
|
523
|
-
catch (error) {
|
|
524
|
-
this.saveError(cumulativePatch, error);
|
|
525
|
-
this.update();
|
|
526
|
-
throw error;
|
|
527
|
-
}
|
|
528
|
-
}));
|
|
529
|
-
this.handleBeforeUnload = (e) => {
|
|
530
|
-
if (this.enqueueSave.isBusy) {
|
|
531
|
-
e.returnValue = false;
|
|
532
|
-
return false;
|
|
533
|
-
}
|
|
534
|
-
};
|
|
535
|
-
this.loaders = [];
|
|
536
|
-
this.db = blank;
|
|
537
|
-
this.base = blank;
|
|
538
|
-
this.tempIdMap = new tempIds_1.TempIdMap(blank);
|
|
539
|
-
this.idMap = this.tempIdMap;
|
|
540
|
-
window.addEventListener('beforeunload', this.handleBeforeUnload);
|
|
541
|
-
}
|
|
542
|
-
/** Saves all committed patches. Useful only if autoSave is off. */
|
|
543
|
-
save() {
|
|
544
|
-
return this.enqueueSave();
|
|
545
|
-
}
|
|
546
|
-
getAutoSave() {
|
|
547
|
-
return this.autoSave;
|
|
548
|
-
}
|
|
549
|
-
setAutoSave(val) {
|
|
550
|
-
if (this.autoSave != val) {
|
|
551
|
-
this.autoSave = val;
|
|
552
|
-
this.autoSave && this.enqueueSave();
|
|
553
|
-
this.update();
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
revert() {
|
|
557
|
-
this.db = this.base;
|
|
558
|
-
this.log = this.log.slice(0, this.savedPoint);
|
|
559
|
-
this.update();
|
|
560
|
-
}
|
|
561
|
-
/** Concrete DbRef instances should override and implement their save logic in this method */
|
|
562
|
-
savePatch(patch) {
|
|
563
|
-
return Promise.resolve({ submit: {} });
|
|
564
|
-
}
|
|
565
|
-
/* Db Update logic */
|
|
566
|
-
commit(patch) {
|
|
567
|
-
const dbBeforeUpdate = this.db.with(patch);
|
|
568
|
-
patch = this.beforeUpdate(patch, dbBeforeUpdate.tables, this.db.tables);
|
|
569
|
-
patch = (0, patchHelpers_1.mergeEntityPatches)(this.db.tables, patch);
|
|
570
|
-
this.log.push({ patch });
|
|
571
|
-
this.db = this.db.with(patch);
|
|
572
|
-
this.autoSave && this.enqueueSave(null, { throttleMs: this.throttleSaveMs });
|
|
573
|
-
this.update();
|
|
574
|
-
}
|
|
575
|
-
beforeUpdate(patch, tables, prevTables) {
|
|
576
|
-
let patchAndDependencies = {};
|
|
577
|
-
(0, utils_1.objectKeys)(tables).filter(entityName => patch[entityName] && !tables[entityName].schema.beforeUpdate).forEach(entityName => {
|
|
578
|
-
patchAndDependencies[entityName] = patch[entityName];
|
|
579
|
-
});
|
|
580
|
-
(0, utils_1.objectKeys)(tables).filter(entityName => patch[entityName] && tables[entityName].schema.beforeUpdate).forEach(entityName => {
|
|
581
|
-
const schema = tables[entityName].schema;
|
|
582
|
-
const { beforeUpdate } = schema;
|
|
583
|
-
let context = {
|
|
584
|
-
clientIdsMap: this.tempIdMap,
|
|
585
|
-
schema,
|
|
586
|
-
tables,
|
|
587
|
-
prevTables,
|
|
588
|
-
};
|
|
589
|
-
const results = patch[entityName].map(e => beforeUpdate(e, context));
|
|
590
|
-
const updatedPatch = { [entityName]: results.map(result => result.entity) };
|
|
591
|
-
const dependentEntityPatchesForUpdate = (0, patchHelpers_1.unionPatches)(results.filter(result => result.dependentEntities).map(result => result.dependentEntities));
|
|
592
|
-
const dependentEntityPatches = this.beforeUpdate(dependentEntityPatchesForUpdate, tables, prevTables);
|
|
593
|
-
patchAndDependencies = (0, patchHelpers_1.mergeEntityPatches)(tables, (0, patchHelpers_1.unionPatches)([patchAndDependencies, updatedPatch, dependentEntityPatches]));
|
|
594
|
-
});
|
|
595
|
-
return patchAndDependencies;
|
|
596
|
-
}
|
|
597
|
-
commitFetch(patch) {
|
|
598
|
-
patch = this.tempIdMap.serverToClientPatch(patch);
|
|
599
|
-
this.rebase(this.base.with(patch));
|
|
600
|
-
this.update();
|
|
601
|
-
}
|
|
602
|
-
rebase(newBase) {
|
|
603
|
-
this.base = newBase;
|
|
604
|
-
this.db = newBase;
|
|
605
|
-
for (let n = this.savedPoint; n < this.log.length; n++) {
|
|
606
|
-
this.db = this.db.with(this.log[n].patch);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
makeCumulativePatch() {
|
|
610
|
-
const lastLogEntry = this.log.length;
|
|
611
|
-
const logEntriesToSave = this.log.slice(this.savedPoint, lastLogEntry);
|
|
612
|
-
let cumulativePatch = (0, patchHelpers_1.makeCumulativePatch)(this.db, logEntriesToSave.map(t => t.patch), this.tempIdMap);
|
|
613
|
-
cumulativePatch = this.tempIdMap.clientToServerPatch(cumulativePatch);
|
|
614
|
-
return cumulativePatch;
|
|
615
|
-
}
|
|
616
|
-
applyResponse(response, newSavedPoint) {
|
|
617
|
-
this.tempIdMap.appendServerMapping(response);
|
|
618
|
-
let serverPatch = (0, patchHelpers_1.flattenResponse)(response.submit, this.db.tables);
|
|
619
|
-
serverPatch = (0, patchHelpers_1.mergeEntityPatches)(this.db.tables, serverPatch);
|
|
620
|
-
this.savedPoint = newSavedPoint;
|
|
621
|
-
this.base = this.db;
|
|
622
|
-
this.commitFetch(serverPatch);
|
|
623
|
-
}
|
|
624
|
-
subscribe(view, params, onUpdate) {
|
|
625
|
-
const id = this.lastSubscriptionId++;
|
|
626
|
-
const subscription = {
|
|
627
|
-
update: (params) => {
|
|
628
|
-
subscription.currentParams = params;
|
|
629
|
-
subscription.currentValue = this.db.runView(view, params);
|
|
630
|
-
return subscription.currentValue;
|
|
631
|
-
},
|
|
632
|
-
currentParams: null,
|
|
633
|
-
currentValue: null,
|
|
634
|
-
onUpdate,
|
|
635
|
-
unsubscribe: () => { this.subscriptions.delete(id); },
|
|
636
|
-
};
|
|
637
|
-
subscription.update(params);
|
|
638
|
-
this.subscriptions.set(id, subscription);
|
|
639
|
-
return subscription;
|
|
640
|
-
}
|
|
641
|
-
onUpdate(handler) {
|
|
642
|
-
this.subscribe({
|
|
643
|
-
compute: db => db,
|
|
644
|
-
compareResults: (a, b) => a === b,
|
|
645
|
-
}, null, handler);
|
|
646
|
-
}
|
|
647
|
-
update() {
|
|
648
|
-
this.subscriptions.forEach(subscription => {
|
|
649
|
-
const previousValue = subscription.currentValue;
|
|
650
|
-
subscription.update(subscription.currentParams);
|
|
651
|
-
if (subscription.currentValue !== previousValue && subscription.onUpdate) {
|
|
652
|
-
subscription.onUpdate(subscription.currentValue);
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
onSaveError(handler) {
|
|
657
|
-
this.saveErrorHandlers.push(handler);
|
|
658
|
-
}
|
|
659
|
-
saveError(request, error) {
|
|
660
|
-
this.saveErrorHandlers.forEach(handler => handler(request, error));
|
|
661
|
-
}
|
|
662
|
-
makeLoader(options) {
|
|
663
|
-
const loader = new Loader_1.Loader(this, () => new SimpleLoadingTracker_1.SimpleLoadingTracker(), options);
|
|
664
|
-
this.loaders.push(loader);
|
|
665
|
-
return loader;
|
|
666
|
-
}
|
|
667
|
-
makeListLoader(options) {
|
|
668
|
-
const loader = new Loader_1.Loader(this, () => new ListLoadingTracker_1.ListLoadingTracker(), options);
|
|
669
|
-
this.loaders.push(loader);
|
|
670
|
-
return loader;
|
|
671
|
-
}
|
|
672
|
-
reload() {
|
|
673
|
-
this.loaders = [];
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
exports.DbRef = DbRef;
|
|
486
|
+
|
|
487
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
488
|
+
exports.DbRef = void 0;
|
|
489
|
+
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
490
|
+
const patchHelpers_1 = __webpack_require__(/*! ./patchHelpers */ "./src/patchHelpers.ts");
|
|
491
|
+
const tempIds_1 = __webpack_require__(/*! ./tempIds */ "./src/tempIds.tsx");
|
|
492
|
+
const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
493
|
+
const lodash_isempty_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.isempty */ "lodash.isempty"));
|
|
494
|
+
const Loader_1 = __webpack_require__(/*! ./Loader */ "./src/Loader.ts");
|
|
495
|
+
const uui_core_1 = __webpack_require__(/*! @epam/uui-core */ "@epam/uui-core");
|
|
496
|
+
const SimpleLoadingTracker_1 = __webpack_require__(/*! ./SimpleLoadingTracker */ "./src/SimpleLoadingTracker.ts");
|
|
497
|
+
const ListLoadingTracker_1 = __webpack_require__(/*! ./ListLoadingTracker */ "./src/ListLoadingTracker.ts");
|
|
498
|
+
class DbRef {
|
|
499
|
+
constructor(blank) {
|
|
500
|
+
this.blank = blank;
|
|
501
|
+
this.log = [];
|
|
502
|
+
this.autoSave = true;
|
|
503
|
+
this.savedPoint = 0;
|
|
504
|
+
this.throttleSaveMs = 1000;
|
|
505
|
+
/* Update subscriptions (aka live views) */
|
|
506
|
+
this.lastSubscriptionId = 1;
|
|
507
|
+
this.subscriptions = new Map();
|
|
508
|
+
this.updateHandlers = [];
|
|
509
|
+
/* Error Subscriptions */
|
|
510
|
+
this.saveErrorHandlers = [];
|
|
511
|
+
/* Save scheduling */
|
|
512
|
+
this.enqueueSave = (0, uui_core_1.batch)(() => (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
|
|
513
|
+
const cumulativePatch = this.makeCumulativePatch();
|
|
514
|
+
const lastLogEntry = this.log.length;
|
|
515
|
+
if ((0, lodash_isempty_1.default)(cumulativePatch)) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
try {
|
|
519
|
+
const response = yield this.savePatch(cumulativePatch);
|
|
520
|
+
this.applyResponse(response, lastLogEntry);
|
|
521
|
+
this.update();
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
this.saveError(cumulativePatch, error);
|
|
525
|
+
this.update();
|
|
526
|
+
throw error;
|
|
527
|
+
}
|
|
528
|
+
}));
|
|
529
|
+
this.handleBeforeUnload = (e) => {
|
|
530
|
+
if (this.enqueueSave.isBusy) {
|
|
531
|
+
e.returnValue = false;
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
this.loaders = [];
|
|
536
|
+
this.db = blank;
|
|
537
|
+
this.base = blank;
|
|
538
|
+
this.tempIdMap = new tempIds_1.TempIdMap(blank);
|
|
539
|
+
this.idMap = this.tempIdMap;
|
|
540
|
+
window.addEventListener('beforeunload', this.handleBeforeUnload);
|
|
541
|
+
}
|
|
542
|
+
/** Saves all committed patches. Useful only if autoSave is off. */
|
|
543
|
+
save() {
|
|
544
|
+
return this.enqueueSave();
|
|
545
|
+
}
|
|
546
|
+
getAutoSave() {
|
|
547
|
+
return this.autoSave;
|
|
548
|
+
}
|
|
549
|
+
setAutoSave(val) {
|
|
550
|
+
if (this.autoSave != val) {
|
|
551
|
+
this.autoSave = val;
|
|
552
|
+
this.autoSave && this.enqueueSave();
|
|
553
|
+
this.update();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
revert() {
|
|
557
|
+
this.db = this.base;
|
|
558
|
+
this.log = this.log.slice(0, this.savedPoint);
|
|
559
|
+
this.update();
|
|
560
|
+
}
|
|
561
|
+
/** Concrete DbRef instances should override and implement their save logic in this method */
|
|
562
|
+
savePatch(patch) {
|
|
563
|
+
return Promise.resolve({ submit: {} });
|
|
564
|
+
}
|
|
565
|
+
/* Db Update logic */
|
|
566
|
+
commit(patch) {
|
|
567
|
+
const dbBeforeUpdate = this.db.with(patch);
|
|
568
|
+
patch = this.beforeUpdate(patch, dbBeforeUpdate.tables, this.db.tables);
|
|
569
|
+
patch = (0, patchHelpers_1.mergeEntityPatches)(this.db.tables, patch);
|
|
570
|
+
this.log.push({ patch });
|
|
571
|
+
this.db = this.db.with(patch);
|
|
572
|
+
this.autoSave && this.enqueueSave(null, { throttleMs: this.throttleSaveMs });
|
|
573
|
+
this.update();
|
|
574
|
+
}
|
|
575
|
+
beforeUpdate(patch, tables, prevTables) {
|
|
576
|
+
let patchAndDependencies = {};
|
|
577
|
+
(0, utils_1.objectKeys)(tables).filter(entityName => patch[entityName] && !tables[entityName].schema.beforeUpdate).forEach(entityName => {
|
|
578
|
+
patchAndDependencies[entityName] = patch[entityName];
|
|
579
|
+
});
|
|
580
|
+
(0, utils_1.objectKeys)(tables).filter(entityName => patch[entityName] && tables[entityName].schema.beforeUpdate).forEach(entityName => {
|
|
581
|
+
const schema = tables[entityName].schema;
|
|
582
|
+
const { beforeUpdate } = schema;
|
|
583
|
+
let context = {
|
|
584
|
+
clientIdsMap: this.tempIdMap,
|
|
585
|
+
schema,
|
|
586
|
+
tables,
|
|
587
|
+
prevTables,
|
|
588
|
+
};
|
|
589
|
+
const results = patch[entityName].map(e => beforeUpdate(e, context));
|
|
590
|
+
const updatedPatch = { [entityName]: results.map(result => result.entity) };
|
|
591
|
+
const dependentEntityPatchesForUpdate = (0, patchHelpers_1.unionPatches)(results.filter(result => result.dependentEntities).map(result => result.dependentEntities));
|
|
592
|
+
const dependentEntityPatches = this.beforeUpdate(dependentEntityPatchesForUpdate, tables, prevTables);
|
|
593
|
+
patchAndDependencies = (0, patchHelpers_1.mergeEntityPatches)(tables, (0, patchHelpers_1.unionPatches)([patchAndDependencies, updatedPatch, dependentEntityPatches]));
|
|
594
|
+
});
|
|
595
|
+
return patchAndDependencies;
|
|
596
|
+
}
|
|
597
|
+
commitFetch(patch) {
|
|
598
|
+
patch = this.tempIdMap.serverToClientPatch(patch);
|
|
599
|
+
this.rebase(this.base.with(patch));
|
|
600
|
+
this.update();
|
|
601
|
+
}
|
|
602
|
+
rebase(newBase) {
|
|
603
|
+
this.base = newBase;
|
|
604
|
+
this.db = newBase;
|
|
605
|
+
for (let n = this.savedPoint; n < this.log.length; n++) {
|
|
606
|
+
this.db = this.db.with(this.log[n].patch);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
makeCumulativePatch() {
|
|
610
|
+
const lastLogEntry = this.log.length;
|
|
611
|
+
const logEntriesToSave = this.log.slice(this.savedPoint, lastLogEntry);
|
|
612
|
+
let cumulativePatch = (0, patchHelpers_1.makeCumulativePatch)(this.db, logEntriesToSave.map(t => t.patch), this.tempIdMap);
|
|
613
|
+
cumulativePatch = this.tempIdMap.clientToServerPatch(cumulativePatch);
|
|
614
|
+
return cumulativePatch;
|
|
615
|
+
}
|
|
616
|
+
applyResponse(response, newSavedPoint) {
|
|
617
|
+
this.tempIdMap.appendServerMapping(response);
|
|
618
|
+
let serverPatch = (0, patchHelpers_1.flattenResponse)(response.submit, this.db.tables);
|
|
619
|
+
serverPatch = (0, patchHelpers_1.mergeEntityPatches)(this.db.tables, serverPatch);
|
|
620
|
+
this.savedPoint = newSavedPoint;
|
|
621
|
+
this.base = this.db;
|
|
622
|
+
this.commitFetch(serverPatch);
|
|
623
|
+
}
|
|
624
|
+
subscribe(view, params, onUpdate) {
|
|
625
|
+
const id = this.lastSubscriptionId++;
|
|
626
|
+
const subscription = {
|
|
627
|
+
update: (params) => {
|
|
628
|
+
subscription.currentParams = params;
|
|
629
|
+
subscription.currentValue = this.db.runView(view, params);
|
|
630
|
+
return subscription.currentValue;
|
|
631
|
+
},
|
|
632
|
+
currentParams: null,
|
|
633
|
+
currentValue: null,
|
|
634
|
+
onUpdate,
|
|
635
|
+
unsubscribe: () => { this.subscriptions.delete(id); },
|
|
636
|
+
};
|
|
637
|
+
subscription.update(params);
|
|
638
|
+
this.subscriptions.set(id, subscription);
|
|
639
|
+
return subscription;
|
|
640
|
+
}
|
|
641
|
+
onUpdate(handler) {
|
|
642
|
+
this.subscribe({
|
|
643
|
+
compute: db => db,
|
|
644
|
+
compareResults: (a, b) => a === b,
|
|
645
|
+
}, null, handler);
|
|
646
|
+
}
|
|
647
|
+
update() {
|
|
648
|
+
this.subscriptions.forEach(subscription => {
|
|
649
|
+
const previousValue = subscription.currentValue;
|
|
650
|
+
subscription.update(subscription.currentParams);
|
|
651
|
+
if (subscription.currentValue !== previousValue && subscription.onUpdate) {
|
|
652
|
+
subscription.onUpdate(subscription.currentValue);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
onSaveError(handler) {
|
|
657
|
+
this.saveErrorHandlers.push(handler);
|
|
658
|
+
}
|
|
659
|
+
saveError(request, error) {
|
|
660
|
+
this.saveErrorHandlers.forEach(handler => handler(request, error));
|
|
661
|
+
}
|
|
662
|
+
makeLoader(options) {
|
|
663
|
+
const loader = new Loader_1.Loader(this, () => new SimpleLoadingTracker_1.SimpleLoadingTracker(), options);
|
|
664
|
+
this.loaders.push(loader);
|
|
665
|
+
return loader;
|
|
666
|
+
}
|
|
667
|
+
makeListLoader(options) {
|
|
668
|
+
const loader = new Loader_1.Loader(this, () => new ListLoadingTracker_1.ListLoadingTracker(), options);
|
|
669
|
+
this.loaders.push(loader);
|
|
670
|
+
return loader;
|
|
671
|
+
}
|
|
672
|
+
reload() {
|
|
673
|
+
this.loaders = [];
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
exports.DbRef = DbRef;
|
|
677
677
|
|
|
678
678
|
|
|
679
679
|
/***/ }),
|
|
@@ -686,211 +686,211 @@ exports.DbRef = DbRef;
|
|
|
686
686
|
/***/ (function(module, exports, __webpack_require__) {
|
|
687
687
|
|
|
688
688
|
"use strict";
|
|
689
|
-
|
|
690
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
691
|
-
exports.DbTable = void 0;
|
|
692
|
-
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
693
|
-
const I = (0, tslib_1.__importStar)(__webpack_require__(/*! immutable */ "immutable"));
|
|
694
|
-
const uui_core_1 = __webpack_require__(/*! @epam/uui-core */ "@epam/uui-core");
|
|
695
|
-
const immutable_1 = __webpack_require__(/*! immutable */ "immutable");
|
|
696
|
-
class DbTable {
|
|
697
|
-
constructor(schema, state, q) {
|
|
698
|
-
this.schema = schema;
|
|
699
|
-
this.state = state;
|
|
700
|
-
this.q = q;
|
|
701
|
-
if (!state) {
|
|
702
|
-
const indexes = (schema.indexes || []).map(indexDef => {
|
|
703
|
-
return {
|
|
704
|
-
field: indexDef,
|
|
705
|
-
map: I.Map(),
|
|
706
|
-
};
|
|
707
|
-
});
|
|
708
|
-
this.state = { pk: I.Map(), indexes };
|
|
709
|
-
}
|
|
710
|
-
if (!q) {
|
|
711
|
-
this.q = {};
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
keyToImmutable(id) {
|
|
715
|
-
if (Array.isArray(id)) {
|
|
716
|
-
return I.List(id);
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
return id;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
byId(id) {
|
|
723
|
-
return this.state.pk.get(this.keyToImmutable(id));
|
|
724
|
-
}
|
|
725
|
-
getId(entity) {
|
|
726
|
-
if (Array.isArray(this.schema.primaryKey)) {
|
|
727
|
-
return this.schema.primaryKey.map((key) => entity[key]);
|
|
728
|
-
}
|
|
729
|
-
return entity[this.schema.primaryKey];
|
|
730
|
-
}
|
|
731
|
-
update(mutate) {
|
|
732
|
-
const clone = new this.constructor(this.schema, this.state, this.q);
|
|
733
|
-
mutate(clone);
|
|
734
|
-
return clone;
|
|
735
|
-
}
|
|
736
|
-
/* Mutation */
|
|
737
|
-
with(patch) {
|
|
738
|
-
const updates = patch.map(entityPatch => {
|
|
739
|
-
const id = this.getId(entityPatch);
|
|
740
|
-
const immId = this.keyToImmutable(id);
|
|
741
|
-
const existing = this.state.pk.get(immId);
|
|
742
|
-
let isDeleted = false;
|
|
743
|
-
let updated = entityPatch;
|
|
744
|
-
if (existing) {
|
|
745
|
-
updated = Object.assign(Object.assign({}, existing), entityPatch);
|
|
746
|
-
if (this.schema.deleteFlag) {
|
|
747
|
-
isDeleted = updated[this.schema.deleteFlag] === true;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
return { id, immId, patch: entityPatch, existing, updated, isNew: !existing, isDeleted };
|
|
751
|
-
});
|
|
752
|
-
const idVal = immutable_1.Seq.Keyed(updates.map(entity => [entity.immId, entity.updated]));
|
|
753
|
-
let newPk = this.state.pk.merge(idVal);
|
|
754
|
-
// TBD: replace with deleteAll after migrating to immutable 4
|
|
755
|
-
updates.filter(u => u.isDeleted).forEach(u => {
|
|
756
|
-
newPk = newPk.delete(u.immId);
|
|
757
|
-
});
|
|
758
|
-
const newIndexes = this.state.indexes.map(index => {
|
|
759
|
-
let newMap = index.map;
|
|
760
|
-
for (let n = 0; n < updates.length; n++) {
|
|
761
|
-
let update = updates[n];
|
|
762
|
-
const newKey = update.updated[index.field];
|
|
763
|
-
if (update.existing) {
|
|
764
|
-
const oldKey = update.existing[index.field];
|
|
765
|
-
if (oldKey !== newKey || update.isDeleted) {
|
|
766
|
-
newMap = newMap.update(oldKey, set => set.remove(update.immId));
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
if (!update.isDeleted) {
|
|
770
|
-
newMap = newMap.update(newKey, I.Set(), set => set.add(update.immId));
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
return Object.assign(Object.assign({}, index), { map: newMap });
|
|
774
|
-
});
|
|
775
|
-
const newState = { pk: newPk, indexes: newIndexes };
|
|
776
|
-
return this.update(t => t.state = newState);
|
|
777
|
-
}
|
|
778
|
-
/* Query */
|
|
779
|
-
find(filter) {
|
|
780
|
-
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { filter: Object.assign(Object.assign({}, this.q.filter), filter) }));
|
|
781
|
-
}
|
|
782
|
-
order(order) {
|
|
783
|
-
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { sorting: order }));
|
|
784
|
-
}
|
|
785
|
-
orderBy(field, direction = 'asc') {
|
|
786
|
-
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { sorting: [{ field, direction }] }));
|
|
787
|
-
}
|
|
788
|
-
thenBy(field, direction) {
|
|
789
|
-
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { sorting: [...t.q.sorting, { field, direction }] }));
|
|
790
|
-
}
|
|
791
|
-
search(text) {
|
|
792
|
-
if (!this.schema.searchBy) {
|
|
793
|
-
throw new Error(`Can't search in the ${this.schema.typeName} table - searchBy is not defined in the schema.`);
|
|
794
|
-
}
|
|
795
|
-
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { search: text }));
|
|
796
|
-
}
|
|
797
|
-
/* Materializing */
|
|
798
|
-
range(from, count) {
|
|
799
|
-
return this.runQuery(Object.assign(Object.assign({}, this.q), { range: { from, count } }));
|
|
800
|
-
}
|
|
801
|
-
count() {
|
|
802
|
-
return this.runQuery(this.q).length;
|
|
803
|
-
}
|
|
804
|
-
one() {
|
|
805
|
-
return this.runQuery(this.q)[0] || null;
|
|
806
|
-
}
|
|
807
|
-
toArray() {
|
|
808
|
-
return this.runQuery(this.q);
|
|
809
|
-
}
|
|
810
|
-
map(fn) {
|
|
811
|
-
return this.toArray().map(fn);
|
|
812
|
-
}
|
|
813
|
-
/* Query implementation */
|
|
814
|
-
runQuery(q) {
|
|
815
|
-
let result = null;
|
|
816
|
-
let filter = q.filter;
|
|
817
|
-
if (filter) {
|
|
818
|
-
let indexes = this.state.indexes;
|
|
819
|
-
// Try to use indexes to fulfill filter conditions
|
|
820
|
-
for (let n = 0; n < indexes.length; n++) {
|
|
821
|
-
const index = indexes[n];
|
|
822
|
-
if (index.field in filter) {
|
|
823
|
-
const _a = filter, _b = index.field, condition = _a[_b], rest = (0, tslib_1.__rest)(_a, [typeof _b === "symbol" ? _b : _b + ""]);
|
|
824
|
-
let conditionValues = null;
|
|
825
|
-
if (condition != null && typeof condition === "object") {
|
|
826
|
-
// Attempt to use index for 'in' and 'isNull' criteria.
|
|
827
|
-
// We need to be very conservative here, indexed field should work the same way as getPatternPredicate. So
|
|
828
|
-
// - it's better to leave corner cases to getPatternPredicate
|
|
829
|
-
const _c = condition, { in: inCriteria, isNull: nullCriteria } = _c, restCriteria = (0, tslib_1.__rest)(_c, ["in", "isNull"]);
|
|
830
|
-
if (inCriteria && Array.isArray(inCriteria)) {
|
|
831
|
-
conditionValues = inCriteria;
|
|
832
|
-
}
|
|
833
|
-
else {
|
|
834
|
-
restCriteria.in = inCriteria;
|
|
835
|
-
}
|
|
836
|
-
// Conditions in getPatternPredicate are composed with 'and' logic,
|
|
837
|
-
// For now, let's not attempt to handle tricky cases like { in: [1,2] isNull: true }, or { in: [1, null] isNull: false }
|
|
838
|
-
// - just leave this to getPatternPredicate.
|
|
839
|
-
// Let's just handle the a most common case { isNull: true } - find all null and undefined
|
|
840
|
-
if (nullCriteria === true && !conditionValues) {
|
|
841
|
-
conditionValues = [null, undefined];
|
|
842
|
-
}
|
|
843
|
-
else {
|
|
844
|
-
restCriteria.in = inCriteria;
|
|
845
|
-
}
|
|
846
|
-
// Keep other conditions, e.g. { in: [1,2,3], isNull: true } - in works via index, isNull - via filter
|
|
847
|
-
if (Object.keys(restCriteria).length > 0) {
|
|
848
|
-
rest[index.field] = restCriteria;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
else {
|
|
852
|
-
conditionValues = [condition];
|
|
853
|
-
}
|
|
854
|
-
if (conditionValues) {
|
|
855
|
-
result = [];
|
|
856
|
-
for (let i = 0; i < conditionValues.length; i++) {
|
|
857
|
-
const idsSet = index.map.get(conditionValues[i]);
|
|
858
|
-
if (idsSet) {
|
|
859
|
-
const idsArray = idsSet.toArray();
|
|
860
|
-
for (let j = 0; j < idsArray.length; j++) {
|
|
861
|
-
const item = this.state.pk.get(idsArray[j]);
|
|
862
|
-
result.push(item);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
filter = Object.keys(rest).length > 0 ? rest : null;
|
|
867
|
-
break;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
if (!result) {
|
|
873
|
-
result = this.state.pk.toArray();
|
|
874
|
-
}
|
|
875
|
-
if (filter) {
|
|
876
|
-
let predicate = (0, uui_core_1.getFilterPredicate)(q.filter);
|
|
877
|
-
result = result.filter(predicate);
|
|
878
|
-
}
|
|
879
|
-
if (q.search) {
|
|
880
|
-
let searchFilter = (0, uui_core_1.getSearchFilter)(q.search);
|
|
881
|
-
result = result.filter(item => searchFilter(this.schema.searchBy.map(field => item[field])));
|
|
882
|
-
}
|
|
883
|
-
if (q.sorting) {
|
|
884
|
-
let comparer = (0, uui_core_1.getOrderComparer)(q.sorting);
|
|
885
|
-
result = result.sort(comparer);
|
|
886
|
-
}
|
|
887
|
-
if (q.range) {
|
|
888
|
-
result = result.slice(q.range.from, q.range.from + q.range.count);
|
|
889
|
-
}
|
|
890
|
-
return result;
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
exports.DbTable = DbTable;
|
|
689
|
+
|
|
690
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
691
|
+
exports.DbTable = void 0;
|
|
692
|
+
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
693
|
+
const I = (0, tslib_1.__importStar)(__webpack_require__(/*! immutable */ "immutable"));
|
|
694
|
+
const uui_core_1 = __webpack_require__(/*! @epam/uui-core */ "@epam/uui-core");
|
|
695
|
+
const immutable_1 = __webpack_require__(/*! immutable */ "immutable");
|
|
696
|
+
class DbTable {
|
|
697
|
+
constructor(schema, state, q) {
|
|
698
|
+
this.schema = schema;
|
|
699
|
+
this.state = state;
|
|
700
|
+
this.q = q;
|
|
701
|
+
if (!state) {
|
|
702
|
+
const indexes = (schema.indexes || []).map(indexDef => {
|
|
703
|
+
return {
|
|
704
|
+
field: indexDef,
|
|
705
|
+
map: I.Map(),
|
|
706
|
+
};
|
|
707
|
+
});
|
|
708
|
+
this.state = { pk: I.Map(), indexes };
|
|
709
|
+
}
|
|
710
|
+
if (!q) {
|
|
711
|
+
this.q = {};
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
keyToImmutable(id) {
|
|
715
|
+
if (Array.isArray(id)) {
|
|
716
|
+
return I.List(id);
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
return id;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
byId(id) {
|
|
723
|
+
return this.state.pk.get(this.keyToImmutable(id));
|
|
724
|
+
}
|
|
725
|
+
getId(entity) {
|
|
726
|
+
if (Array.isArray(this.schema.primaryKey)) {
|
|
727
|
+
return this.schema.primaryKey.map((key) => entity[key]);
|
|
728
|
+
}
|
|
729
|
+
return entity[this.schema.primaryKey];
|
|
730
|
+
}
|
|
731
|
+
update(mutate) {
|
|
732
|
+
const clone = new this.constructor(this.schema, this.state, this.q);
|
|
733
|
+
mutate(clone);
|
|
734
|
+
return clone;
|
|
735
|
+
}
|
|
736
|
+
/* Mutation */
|
|
737
|
+
with(patch) {
|
|
738
|
+
const updates = patch.map(entityPatch => {
|
|
739
|
+
const id = this.getId(entityPatch);
|
|
740
|
+
const immId = this.keyToImmutable(id);
|
|
741
|
+
const existing = this.state.pk.get(immId);
|
|
742
|
+
let isDeleted = false;
|
|
743
|
+
let updated = entityPatch;
|
|
744
|
+
if (existing) {
|
|
745
|
+
updated = Object.assign(Object.assign({}, existing), entityPatch);
|
|
746
|
+
if (this.schema.deleteFlag) {
|
|
747
|
+
isDeleted = updated[this.schema.deleteFlag] === true;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return { id, immId, patch: entityPatch, existing, updated, isNew: !existing, isDeleted };
|
|
751
|
+
});
|
|
752
|
+
const idVal = immutable_1.Seq.Keyed(updates.map(entity => [entity.immId, entity.updated]));
|
|
753
|
+
let newPk = this.state.pk.merge(idVal);
|
|
754
|
+
// TBD: replace with deleteAll after migrating to immutable 4
|
|
755
|
+
updates.filter(u => u.isDeleted).forEach(u => {
|
|
756
|
+
newPk = newPk.delete(u.immId);
|
|
757
|
+
});
|
|
758
|
+
const newIndexes = this.state.indexes.map(index => {
|
|
759
|
+
let newMap = index.map;
|
|
760
|
+
for (let n = 0; n < updates.length; n++) {
|
|
761
|
+
let update = updates[n];
|
|
762
|
+
const newKey = update.updated[index.field];
|
|
763
|
+
if (update.existing) {
|
|
764
|
+
const oldKey = update.existing[index.field];
|
|
765
|
+
if (oldKey !== newKey || update.isDeleted) {
|
|
766
|
+
newMap = newMap.update(oldKey, set => set.remove(update.immId));
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (!update.isDeleted) {
|
|
770
|
+
newMap = newMap.update(newKey, I.Set(), set => set.add(update.immId));
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return Object.assign(Object.assign({}, index), { map: newMap });
|
|
774
|
+
});
|
|
775
|
+
const newState = { pk: newPk, indexes: newIndexes };
|
|
776
|
+
return this.update(t => t.state = newState);
|
|
777
|
+
}
|
|
778
|
+
/* Query */
|
|
779
|
+
find(filter) {
|
|
780
|
+
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { filter: Object.assign(Object.assign({}, this.q.filter), filter) }));
|
|
781
|
+
}
|
|
782
|
+
order(order) {
|
|
783
|
+
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { sorting: order }));
|
|
784
|
+
}
|
|
785
|
+
orderBy(field, direction = 'asc') {
|
|
786
|
+
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { sorting: [{ field, direction }] }));
|
|
787
|
+
}
|
|
788
|
+
thenBy(field, direction) {
|
|
789
|
+
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { sorting: [...t.q.sorting, { field, direction }] }));
|
|
790
|
+
}
|
|
791
|
+
search(text) {
|
|
792
|
+
if (!this.schema.searchBy) {
|
|
793
|
+
throw new Error(`Can't search in the ${this.schema.typeName} table - searchBy is not defined in the schema.`);
|
|
794
|
+
}
|
|
795
|
+
return this.update(t => t.q = Object.assign(Object.assign({}, t.q), { search: text }));
|
|
796
|
+
}
|
|
797
|
+
/* Materializing */
|
|
798
|
+
range(from, count) {
|
|
799
|
+
return this.runQuery(Object.assign(Object.assign({}, this.q), { range: { from, count } }));
|
|
800
|
+
}
|
|
801
|
+
count() {
|
|
802
|
+
return this.runQuery(this.q).length;
|
|
803
|
+
}
|
|
804
|
+
one() {
|
|
805
|
+
return this.runQuery(this.q)[0] || null;
|
|
806
|
+
}
|
|
807
|
+
toArray() {
|
|
808
|
+
return this.runQuery(this.q);
|
|
809
|
+
}
|
|
810
|
+
map(fn) {
|
|
811
|
+
return this.toArray().map(fn);
|
|
812
|
+
}
|
|
813
|
+
/* Query implementation */
|
|
814
|
+
runQuery(q) {
|
|
815
|
+
let result = null;
|
|
816
|
+
let filter = q.filter;
|
|
817
|
+
if (filter) {
|
|
818
|
+
let indexes = this.state.indexes;
|
|
819
|
+
// Try to use indexes to fulfill filter conditions
|
|
820
|
+
for (let n = 0; n < indexes.length; n++) {
|
|
821
|
+
const index = indexes[n];
|
|
822
|
+
if (index.field in filter) {
|
|
823
|
+
const _a = filter, _b = index.field, condition = _a[_b], rest = (0, tslib_1.__rest)(_a, [typeof _b === "symbol" ? _b : _b + ""]);
|
|
824
|
+
let conditionValues = null;
|
|
825
|
+
if (condition != null && typeof condition === "object") {
|
|
826
|
+
// Attempt to use index for 'in' and 'isNull' criteria.
|
|
827
|
+
// We need to be very conservative here, indexed field should work the same way as getPatternPredicate. So
|
|
828
|
+
// - it's better to leave corner cases to getPatternPredicate
|
|
829
|
+
const _c = condition, { in: inCriteria, isNull: nullCriteria } = _c, restCriteria = (0, tslib_1.__rest)(_c, ["in", "isNull"]);
|
|
830
|
+
if (inCriteria && Array.isArray(inCriteria)) {
|
|
831
|
+
conditionValues = inCriteria;
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
restCriteria.in = inCriteria;
|
|
835
|
+
}
|
|
836
|
+
// Conditions in getPatternPredicate are composed with 'and' logic,
|
|
837
|
+
// For now, let's not attempt to handle tricky cases like { in: [1,2] isNull: true }, or { in: [1, null] isNull: false }
|
|
838
|
+
// - just leave this to getPatternPredicate.
|
|
839
|
+
// Let's just handle the a most common case { isNull: true } - find all null and undefined
|
|
840
|
+
if (nullCriteria === true && !conditionValues) {
|
|
841
|
+
conditionValues = [null, undefined];
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
restCriteria.in = inCriteria;
|
|
845
|
+
}
|
|
846
|
+
// Keep other conditions, e.g. { in: [1,2,3], isNull: true } - in works via index, isNull - via filter
|
|
847
|
+
if (Object.keys(restCriteria).length > 0) {
|
|
848
|
+
rest[index.field] = restCriteria;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
conditionValues = [condition];
|
|
853
|
+
}
|
|
854
|
+
if (conditionValues) {
|
|
855
|
+
result = [];
|
|
856
|
+
for (let i = 0; i < conditionValues.length; i++) {
|
|
857
|
+
const idsSet = index.map.get(conditionValues[i]);
|
|
858
|
+
if (idsSet) {
|
|
859
|
+
const idsArray = idsSet.toArray();
|
|
860
|
+
for (let j = 0; j < idsArray.length; j++) {
|
|
861
|
+
const item = this.state.pk.get(idsArray[j]);
|
|
862
|
+
result.push(item);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
filter = Object.keys(rest).length > 0 ? rest : null;
|
|
867
|
+
break;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
if (!result) {
|
|
873
|
+
result = this.state.pk.toArray();
|
|
874
|
+
}
|
|
875
|
+
if (filter) {
|
|
876
|
+
let predicate = (0, uui_core_1.getFilterPredicate)(q.filter);
|
|
877
|
+
result = result.filter(predicate);
|
|
878
|
+
}
|
|
879
|
+
if (q.search) {
|
|
880
|
+
let searchFilter = (0, uui_core_1.getSearchFilter)(q.search);
|
|
881
|
+
result = result.filter(item => searchFilter(this.schema.searchBy.map(field => item[field])));
|
|
882
|
+
}
|
|
883
|
+
if (q.sorting) {
|
|
884
|
+
let comparer = (0, uui_core_1.getOrderComparer)(q.sorting);
|
|
885
|
+
result = result.sort(comparer);
|
|
886
|
+
}
|
|
887
|
+
if (q.range) {
|
|
888
|
+
result = result.slice(q.range.from, q.range.from + q.range.count);
|
|
889
|
+
}
|
|
890
|
+
return result;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
exports.DbTable = DbTable;
|
|
894
894
|
|
|
895
895
|
|
|
896
896
|
/***/ }),
|
|
@@ -903,98 +903,98 @@ exports.DbTable = DbTable;
|
|
|
903
903
|
/***/ (function(module, exports, __webpack_require__) {
|
|
904
904
|
|
|
905
905
|
"use strict";
|
|
906
|
-
|
|
907
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
908
|
-
exports.ListLoadingTracker = void 0;
|
|
909
|
-
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
910
|
-
class ListLoadingTracker {
|
|
911
|
-
constructor(options) {
|
|
912
|
-
this.options = options;
|
|
913
|
-
this.cache = new Map();
|
|
914
|
-
this.extractList = (options === null || options === void 0 ? void 0 : options.extractList) || ((r) => r);
|
|
915
|
-
}
|
|
916
|
-
prepare(request) {
|
|
917
|
-
const { filter, sorting, search, range } = request, unknownFields = (0, tslib_1.__rest)(request, ["filter", "sorting", "search", "range"]);
|
|
918
|
-
const options = {};
|
|
919
|
-
if (filter) {
|
|
920
|
-
options.filter = filter;
|
|
921
|
-
}
|
|
922
|
-
if (sorting) {
|
|
923
|
-
options.sorting = sorting;
|
|
924
|
-
}
|
|
925
|
-
if (search) {
|
|
926
|
-
options.search = search;
|
|
927
|
-
}
|
|
928
|
-
let keyObject = Object.assign(Object.assign({}, options), unknownFields);
|
|
929
|
-
const key = JSON.stringify(keyObject);
|
|
930
|
-
let entry = this.cache.get(key);
|
|
931
|
-
if (!entry) {
|
|
932
|
-
entry = { options, count: 0, maxCount: null, isComplete: false };
|
|
933
|
-
this.cache.set(key, entry);
|
|
934
|
-
}
|
|
935
|
-
return { options, range, unknownFields, entry };
|
|
936
|
-
}
|
|
937
|
-
diff(request) {
|
|
938
|
-
const { options, range, unknownFields, entry } = this.prepare(request);
|
|
939
|
-
if (entry.isComplete) {
|
|
940
|
-
return null;
|
|
941
|
-
}
|
|
942
|
-
if (!request.range) {
|
|
943
|
-
return request;
|
|
944
|
-
}
|
|
945
|
-
else {
|
|
946
|
-
const requestedMax = request.range.from + request.range.count;
|
|
947
|
-
let missingCount = requestedMax - entry.count;
|
|
948
|
-
if (entry.maxCount) {
|
|
949
|
-
missingCount = Math.min(missingCount, entry.maxCount);
|
|
950
|
-
}
|
|
951
|
-
if (missingCount > 0) {
|
|
952
|
-
return Object.assign(Object.assign(Object.assign({}, options), unknownFields), { range: { from: entry.count, count: requestedMax - entry.count } });
|
|
953
|
-
}
|
|
954
|
-
else {
|
|
955
|
-
return null;
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
count(request) {
|
|
960
|
-
const { entry } = this.prepare(request);
|
|
961
|
-
return { knownCount: Math.max(entry.count, entry.maxCount), exactCount: entry.maxCount };
|
|
962
|
-
}
|
|
963
|
-
append(request, response) {
|
|
964
|
-
const { options, range, unknownFields, entry } = this.prepare(request);
|
|
965
|
-
if (response) {
|
|
966
|
-
const list = this.extractList(response);
|
|
967
|
-
if (list.count) {
|
|
968
|
-
entry.maxCount = list.count;
|
|
969
|
-
}
|
|
970
|
-
if (!range) {
|
|
971
|
-
entry.count = list.items.length;
|
|
972
|
-
entry.maxCount = list.items.length;
|
|
973
|
-
entry.isComplete = true;
|
|
974
|
-
}
|
|
975
|
-
else {
|
|
976
|
-
const from = (list.from == null) ? range.from : list.from;
|
|
977
|
-
if (list.items.length > 0) {
|
|
978
|
-
entry.count = Math.max(entry.count, from + list.items.length);
|
|
979
|
-
}
|
|
980
|
-
if (list.items.length < range.count) {
|
|
981
|
-
entry.maxCount = from + list.items.length;
|
|
982
|
-
entry.count = entry.maxCount;
|
|
983
|
-
entry.isComplete = true;
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
else {
|
|
988
|
-
if (range) {
|
|
989
|
-
entry.count = Math.max(entry.count, request.range.from + request.range.count);
|
|
990
|
-
}
|
|
991
|
-
else {
|
|
992
|
-
entry.isComplete = true;
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
exports.ListLoadingTracker = ListLoadingTracker;
|
|
906
|
+
|
|
907
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
908
|
+
exports.ListLoadingTracker = void 0;
|
|
909
|
+
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
910
|
+
class ListLoadingTracker {
|
|
911
|
+
constructor(options) {
|
|
912
|
+
this.options = options;
|
|
913
|
+
this.cache = new Map();
|
|
914
|
+
this.extractList = (options === null || options === void 0 ? void 0 : options.extractList) || ((r) => r);
|
|
915
|
+
}
|
|
916
|
+
prepare(request) {
|
|
917
|
+
const { filter, sorting, search, range } = request, unknownFields = (0, tslib_1.__rest)(request, ["filter", "sorting", "search", "range"]);
|
|
918
|
+
const options = {};
|
|
919
|
+
if (filter) {
|
|
920
|
+
options.filter = filter;
|
|
921
|
+
}
|
|
922
|
+
if (sorting) {
|
|
923
|
+
options.sorting = sorting;
|
|
924
|
+
}
|
|
925
|
+
if (search) {
|
|
926
|
+
options.search = search;
|
|
927
|
+
}
|
|
928
|
+
let keyObject = Object.assign(Object.assign({}, options), unknownFields);
|
|
929
|
+
const key = JSON.stringify(keyObject);
|
|
930
|
+
let entry = this.cache.get(key);
|
|
931
|
+
if (!entry) {
|
|
932
|
+
entry = { options, count: 0, maxCount: null, isComplete: false };
|
|
933
|
+
this.cache.set(key, entry);
|
|
934
|
+
}
|
|
935
|
+
return { options, range, unknownFields, entry };
|
|
936
|
+
}
|
|
937
|
+
diff(request) {
|
|
938
|
+
const { options, range, unknownFields, entry } = this.prepare(request);
|
|
939
|
+
if (entry.isComplete) {
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
if (!request.range) {
|
|
943
|
+
return request;
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
const requestedMax = request.range.from + request.range.count;
|
|
947
|
+
let missingCount = requestedMax - entry.count;
|
|
948
|
+
if (entry.maxCount) {
|
|
949
|
+
missingCount = Math.min(missingCount, entry.maxCount);
|
|
950
|
+
}
|
|
951
|
+
if (missingCount > 0) {
|
|
952
|
+
return Object.assign(Object.assign(Object.assign({}, options), unknownFields), { range: { from: entry.count, count: requestedMax - entry.count } });
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
count(request) {
|
|
960
|
+
const { entry } = this.prepare(request);
|
|
961
|
+
return { knownCount: Math.max(entry.count, entry.maxCount), exactCount: entry.maxCount };
|
|
962
|
+
}
|
|
963
|
+
append(request, response) {
|
|
964
|
+
const { options, range, unknownFields, entry } = this.prepare(request);
|
|
965
|
+
if (response) {
|
|
966
|
+
const list = this.extractList(response);
|
|
967
|
+
if (list.count) {
|
|
968
|
+
entry.maxCount = list.count;
|
|
969
|
+
}
|
|
970
|
+
if (!range) {
|
|
971
|
+
entry.count = list.items.length;
|
|
972
|
+
entry.maxCount = list.items.length;
|
|
973
|
+
entry.isComplete = true;
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
const from = (list.from == null) ? range.from : list.from;
|
|
977
|
+
if (list.items.length > 0) {
|
|
978
|
+
entry.count = Math.max(entry.count, from + list.items.length);
|
|
979
|
+
}
|
|
980
|
+
if (list.items.length < range.count) {
|
|
981
|
+
entry.maxCount = from + list.items.length;
|
|
982
|
+
entry.count = entry.maxCount;
|
|
983
|
+
entry.isComplete = true;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
else {
|
|
988
|
+
if (range) {
|
|
989
|
+
entry.count = Math.max(entry.count, request.range.from + request.range.count);
|
|
990
|
+
}
|
|
991
|
+
else {
|
|
992
|
+
entry.isComplete = true;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
exports.ListLoadingTracker = ListLoadingTracker;
|
|
998
998
|
|
|
999
999
|
|
|
1000
1000
|
/***/ }),
|
|
@@ -1007,81 +1007,81 @@ exports.ListLoadingTracker = ListLoadingTracker;
|
|
|
1007
1007
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1008
1008
|
|
|
1009
1009
|
"use strict";
|
|
1010
|
-
|
|
1011
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1012
|
-
exports.Loader = void 0;
|
|
1013
|
-
class Loader {
|
|
1014
|
-
constructor(dbRef, getTracker, options) {
|
|
1015
|
-
this.dbRef = dbRef;
|
|
1016
|
-
this.getTracker = getTracker;
|
|
1017
|
-
this.options = options;
|
|
1018
|
-
this.currentRequests = new Set();
|
|
1019
|
-
this.load = (inputRequest) => {
|
|
1020
|
-
const result = {
|
|
1021
|
-
isComplete: this.loaded.diff(inputRequest) == null,
|
|
1022
|
-
isLoading: false,
|
|
1023
|
-
request: inputRequest,
|
|
1024
|
-
missing: this.loadedAndLoading.diff(inputRequest),
|
|
1025
|
-
promise: null,
|
|
1026
|
-
reload: null,
|
|
1027
|
-
};
|
|
1028
|
-
result.reload = () => {
|
|
1029
|
-
result.missing = inputRequest;
|
|
1030
|
-
this.loadImpl(result);
|
|
1031
|
-
};
|
|
1032
|
-
if (result.isComplete) {
|
|
1033
|
-
// Everything requested is already loaded
|
|
1034
|
-
result.isComplete = true;
|
|
1035
|
-
result.isLoading = false;
|
|
1036
|
-
result.promise = Promise.resolve();
|
|
1037
|
-
return result;
|
|
1038
|
-
}
|
|
1039
|
-
else {
|
|
1040
|
-
if (result.missing == null) {
|
|
1041
|
-
// Everything requested is either loaded already, or being loaded
|
|
1042
|
-
result.isComplete = false;
|
|
1043
|
-
result.isLoading = true;
|
|
1044
|
-
// There's no good way to tell which of current requests is querying requested data - there actually can be 2 of them.
|
|
1045
|
-
// So we track all running requests, and await all of them in such case.
|
|
1046
|
-
result.promise = Promise.all(this.currentRequests.entries()).then(() => { });
|
|
1047
|
-
return result;
|
|
1048
|
-
}
|
|
1049
|
-
else {
|
|
1050
|
-
// Something requested is not loaded, nor being loaded. Need to perform a new request.
|
|
1051
|
-
this.loadImpl(result);
|
|
1052
|
-
return result;
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
|
-
this.loaded = getTracker();
|
|
1057
|
-
this.loadedAndLoading = getTracker();
|
|
1058
|
-
}
|
|
1059
|
-
loadImpl(state) {
|
|
1060
|
-
this.loadedAndLoading.append(state.missing);
|
|
1061
|
-
state.isLoading = true;
|
|
1062
|
-
let serverRequest = state.missing;
|
|
1063
|
-
if (this.options.clientToServerRequest) {
|
|
1064
|
-
serverRequest = this.options.clientToServerRequest(serverRequest);
|
|
1065
|
-
}
|
|
1066
|
-
state.promise = this.options.api(serverRequest).then(result => {
|
|
1067
|
-
this.loaded.append(state.missing, result);
|
|
1068
|
-
this.loadedAndLoading.append(state.missing, result);
|
|
1069
|
-
state.missing = null;
|
|
1070
|
-
state.isLoading = false;
|
|
1071
|
-
state.isComplete = true;
|
|
1072
|
-
state.promise = Promise.resolve();
|
|
1073
|
-
const flatten = this.options.convertToPatch(result);
|
|
1074
|
-
this.options.postProcess && this.options.postProcess(flatten);
|
|
1075
|
-
this.dbRef.commitFetch(flatten);
|
|
1076
|
-
});
|
|
1077
|
-
this.currentRequests.add(state.promise);
|
|
1078
|
-
state.promise.finally(() => {
|
|
1079
|
-
this.currentRequests.delete(state.promise);
|
|
1080
|
-
});
|
|
1081
|
-
return state.promise;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
exports.Loader = Loader;
|
|
1010
|
+
|
|
1011
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1012
|
+
exports.Loader = void 0;
|
|
1013
|
+
class Loader {
|
|
1014
|
+
constructor(dbRef, getTracker, options) {
|
|
1015
|
+
this.dbRef = dbRef;
|
|
1016
|
+
this.getTracker = getTracker;
|
|
1017
|
+
this.options = options;
|
|
1018
|
+
this.currentRequests = new Set();
|
|
1019
|
+
this.load = (inputRequest) => {
|
|
1020
|
+
const result = {
|
|
1021
|
+
isComplete: this.loaded.diff(inputRequest) == null,
|
|
1022
|
+
isLoading: false,
|
|
1023
|
+
request: inputRequest,
|
|
1024
|
+
missing: this.loadedAndLoading.diff(inputRequest),
|
|
1025
|
+
promise: null,
|
|
1026
|
+
reload: null,
|
|
1027
|
+
};
|
|
1028
|
+
result.reload = () => {
|
|
1029
|
+
result.missing = inputRequest;
|
|
1030
|
+
this.loadImpl(result);
|
|
1031
|
+
};
|
|
1032
|
+
if (result.isComplete) {
|
|
1033
|
+
// Everything requested is already loaded
|
|
1034
|
+
result.isComplete = true;
|
|
1035
|
+
result.isLoading = false;
|
|
1036
|
+
result.promise = Promise.resolve();
|
|
1037
|
+
return result;
|
|
1038
|
+
}
|
|
1039
|
+
else {
|
|
1040
|
+
if (result.missing == null) {
|
|
1041
|
+
// Everything requested is either loaded already, or being loaded
|
|
1042
|
+
result.isComplete = false;
|
|
1043
|
+
result.isLoading = true;
|
|
1044
|
+
// There's no good way to tell which of current requests is querying requested data - there actually can be 2 of them.
|
|
1045
|
+
// So we track all running requests, and await all of them in such case.
|
|
1046
|
+
result.promise = Promise.all(this.currentRequests.entries()).then(() => { });
|
|
1047
|
+
return result;
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
// Something requested is not loaded, nor being loaded. Need to perform a new request.
|
|
1051
|
+
this.loadImpl(result);
|
|
1052
|
+
return result;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
this.loaded = getTracker();
|
|
1057
|
+
this.loadedAndLoading = getTracker();
|
|
1058
|
+
}
|
|
1059
|
+
loadImpl(state) {
|
|
1060
|
+
this.loadedAndLoading.append(state.missing);
|
|
1061
|
+
state.isLoading = true;
|
|
1062
|
+
let serverRequest = state.missing;
|
|
1063
|
+
if (this.options.clientToServerRequest) {
|
|
1064
|
+
serverRequest = this.options.clientToServerRequest(serverRequest);
|
|
1065
|
+
}
|
|
1066
|
+
state.promise = this.options.api(serverRequest).then(result => {
|
|
1067
|
+
this.loaded.append(state.missing, result);
|
|
1068
|
+
this.loadedAndLoading.append(state.missing, result);
|
|
1069
|
+
state.missing = null;
|
|
1070
|
+
state.isLoading = false;
|
|
1071
|
+
state.isComplete = true;
|
|
1072
|
+
state.promise = Promise.resolve();
|
|
1073
|
+
const flatten = this.options.convertToPatch(result);
|
|
1074
|
+
this.options.postProcess && this.options.postProcess(flatten);
|
|
1075
|
+
this.dbRef.commitFetch(flatten);
|
|
1076
|
+
});
|
|
1077
|
+
this.currentRequests.add(state.promise);
|
|
1078
|
+
state.promise.finally(() => {
|
|
1079
|
+
this.currentRequests.delete(state.promise);
|
|
1080
|
+
});
|
|
1081
|
+
return state.promise;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
exports.Loader = Loader;
|
|
1085
1085
|
|
|
1086
1086
|
|
|
1087
1087
|
/***/ }),
|
|
@@ -1094,24 +1094,24 @@ exports.Loader = Loader;
|
|
|
1094
1094
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1095
1095
|
|
|
1096
1096
|
"use strict";
|
|
1097
|
-
|
|
1098
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1099
|
-
exports.SimpleLoadingTracker = void 0;
|
|
1100
|
-
class SimpleLoadingTracker {
|
|
1101
|
-
constructor() {
|
|
1102
|
-
this.set = new Set();
|
|
1103
|
-
}
|
|
1104
|
-
getKey(request) {
|
|
1105
|
-
return JSON.stringify(request);
|
|
1106
|
-
}
|
|
1107
|
-
diff(request) {
|
|
1108
|
-
return this.set.has(this.getKey(request)) ? null : request;
|
|
1109
|
-
}
|
|
1110
|
-
append(request, result) {
|
|
1111
|
-
this.set.add(this.getKey(request));
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
exports.SimpleLoadingTracker = SimpleLoadingTracker;
|
|
1097
|
+
|
|
1098
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1099
|
+
exports.SimpleLoadingTracker = void 0;
|
|
1100
|
+
class SimpleLoadingTracker {
|
|
1101
|
+
constructor() {
|
|
1102
|
+
this.set = new Set();
|
|
1103
|
+
}
|
|
1104
|
+
getKey(request) {
|
|
1105
|
+
return JSON.stringify(request);
|
|
1106
|
+
}
|
|
1107
|
+
diff(request) {
|
|
1108
|
+
return this.set.has(this.getKey(request)) ? null : request;
|
|
1109
|
+
}
|
|
1110
|
+
append(request, result) {
|
|
1111
|
+
this.set.add(this.getKey(request));
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
exports.SimpleLoadingTracker = SimpleLoadingTracker;
|
|
1115
1115
|
|
|
1116
1116
|
|
|
1117
1117
|
/***/ }),
|
|
@@ -1124,17 +1124,17 @@ exports.SimpleLoadingTracker = SimpleLoadingTracker;
|
|
|
1124
1124
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1125
1125
|
|
|
1126
1126
|
"use strict";
|
|
1127
|
-
|
|
1128
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1129
|
-
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
1130
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./types */ "./src/types.ts"), exports);
|
|
1131
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./Db */ "./src/Db.ts"), exports);
|
|
1132
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./DbTable */ "./src/DbTable.ts"), exports);
|
|
1133
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./DbRef */ "./src/DbRef.ts"), exports);
|
|
1134
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./tempIds */ "./src/tempIds.tsx"), exports);
|
|
1135
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./utils */ "./src/utils.ts"), exports);
|
|
1136
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./patchHelpers */ "./src/patchHelpers.ts"), exports);
|
|
1137
|
-
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./reactHooks */ "./src/reactHooks.ts"), exports);
|
|
1127
|
+
|
|
1128
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1129
|
+
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
1130
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./types */ "./src/types.ts"), exports);
|
|
1131
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./Db */ "./src/Db.ts"), exports);
|
|
1132
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./DbTable */ "./src/DbTable.ts"), exports);
|
|
1133
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./DbRef */ "./src/DbRef.ts"), exports);
|
|
1134
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./tempIds */ "./src/tempIds.tsx"), exports);
|
|
1135
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./utils */ "./src/utils.ts"), exports);
|
|
1136
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./patchHelpers */ "./src/patchHelpers.ts"), exports);
|
|
1137
|
+
(0, tslib_1.__exportStar)(__webpack_require__(/*! ./reactHooks */ "./src/reactHooks.ts"), exports);
|
|
1138
1138
|
|
|
1139
1139
|
|
|
1140
1140
|
/***/ }),
|
|
@@ -1147,195 +1147,195 @@ const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es
|
|
|
1147
1147
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1148
1148
|
|
|
1149
1149
|
"use strict";
|
|
1150
|
-
|
|
1151
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1152
|
-
exports.getEntityParentDependencies = exports.getParentEntities = exports.hasServerFields = exports.isNew = exports.getId = exports.isClientOnly = exports.makeCumulativePatch = exports.isPatchBlank = exports.flattenResponse = exports.mergeEntityPatches = exports.unionPatches = void 0;
|
|
1153
|
-
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
1154
|
-
const types_1 = __webpack_require__(/*! ./types */ "./src/types.ts");
|
|
1155
|
-
const I = (0, tslib_1.__importStar)(__webpack_require__(/*! immutable */ "immutable"));
|
|
1156
|
-
const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
1157
|
-
const lodash_groupby_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.groupby */ "lodash.groupby"));
|
|
1158
|
-
const lodash_map_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.map */ "lodash.map"));
|
|
1159
|
-
const lodash_merge_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.merge */ "lodash.merge"));
|
|
1160
|
-
const lodash_countby_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.countby */ "lodash.countby"));
|
|
1161
|
-
const lodash_filter_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.filter */ "lodash.filter"));
|
|
1162
|
-
const lodash_foreach_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.foreach */ "lodash.foreach"));
|
|
1163
|
-
const lodash_pick_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.pick */ "lodash.pick"));
|
|
1164
|
-
function unionPatches(patches) {
|
|
1165
|
-
const result = {};
|
|
1166
|
-
patches.forEach(patch => {
|
|
1167
|
-
(0, utils_1.objectKeys)(patch).forEach(tableName => {
|
|
1168
|
-
result[tableName] = result[tableName] ? [...result[tableName], ...patch[tableName]] : patch[tableName];
|
|
1169
|
-
});
|
|
1170
|
-
});
|
|
1171
|
-
return result;
|
|
1172
|
-
}
|
|
1173
|
-
exports.unionPatches = unionPatches;
|
|
1174
|
-
function mergeEntityPatches(tables, patch) {
|
|
1175
|
-
const result = {};
|
|
1176
|
-
(0, utils_1.objectKeys)(patch).forEach(key => {
|
|
1177
|
-
const table = tables[key];
|
|
1178
|
-
const groupedEntities = (0, lodash_groupby_1.default)(patch[key], item => table.getId(item));
|
|
1179
|
-
const tablePatch = (0, lodash_map_1.default)(groupedEntities, entities => {
|
|
1180
|
-
const result = (0, lodash_merge_1.default)({}, ...entities);
|
|
1181
|
-
if (true) {
|
|
1182
|
-
const props = (0, utils_1.objectKeys)(result).filter(prop => Object.keys((0, lodash_countby_1.default)(entities.filter(entity => entity[prop] !== undefined), entity => entity[prop])).length > 1);
|
|
1183
|
-
if (props.length) {
|
|
1184
|
-
console.warn('mergeEntityPatches: Different property values found for the same entity.\nProperty names:\n', props, '\nEntities:\n', ...entities);
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
return result;
|
|
1188
|
-
});
|
|
1189
|
-
result[key] = tablePatch;
|
|
1190
|
-
});
|
|
1191
|
-
return result;
|
|
1192
|
-
}
|
|
1193
|
-
exports.mergeEntityPatches = mergeEntityPatches;
|
|
1194
|
-
function flattenResponseHelper(result, object, tablesByTypeName) {
|
|
1195
|
-
if (Array.isArray(object)) {
|
|
1196
|
-
object.forEach(item => flattenResponseHelper(result, item, tablesByTypeName));
|
|
1197
|
-
}
|
|
1198
|
-
else if (typeof object === 'object') {
|
|
1199
|
-
const clone = {};
|
|
1200
|
-
let tableKey;
|
|
1201
|
-
let fields = {};
|
|
1202
|
-
if ('__typename' in object) {
|
|
1203
|
-
const table = tablesByTypeName[object.__typename];
|
|
1204
|
-
if (table) {
|
|
1205
|
-
tableKey = table.schema.tableName;
|
|
1206
|
-
fields = table.schema.fields;
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
for (let key in object) {
|
|
1210
|
-
const value = object[key];
|
|
1211
|
-
const field = fields[key];
|
|
1212
|
-
if ((field && !!field.disableFlatten)
|
|
1213
|
-
|| typeof value !== 'object'
|
|
1214
|
-
|| value === null
|
|
1215
|
-
|| (Array.isArray(value) && value.every(item => typeof item !== 'object'))) {
|
|
1216
|
-
clone[key] = value;
|
|
1217
|
-
}
|
|
1218
|
-
else {
|
|
1219
|
-
flattenResponseHelper(result, value, tablesByTypeName);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
if (tableKey) {
|
|
1223
|
-
result[tableKey].push(clone);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
function flattenResponse(response, tables) {
|
|
1228
|
-
const result = {};
|
|
1229
|
-
let tablesByTypeName = {};
|
|
1230
|
-
Object.values(tables).forEach(table => tablesByTypeName[table.schema.typeName] = table);
|
|
1231
|
-
(0, utils_1.objectKeys)(tables).forEach(tableName => tablesByTypeName);
|
|
1232
|
-
(0, utils_1.objectKeys)(tables).forEach(key => result[key] = []);
|
|
1233
|
-
flattenResponseHelper(result, response, tablesByTypeName);
|
|
1234
|
-
return result;
|
|
1235
|
-
}
|
|
1236
|
-
exports.flattenResponse = flattenResponse;
|
|
1237
|
-
function isPatchBlank(patch) {
|
|
1238
|
-
return !(0, utils_1.objectKeys)(patch).some(key => patch[key].length > 0);
|
|
1239
|
-
}
|
|
1240
|
-
exports.isPatchBlank = isPatchBlank;
|
|
1241
|
-
/**
|
|
1242
|
-
* Given several patches, creates a new single patch with all fields from all entities in all patches.
|
|
1243
|
-
*/
|
|
1244
|
-
function makeCumulativePatch(updatedDb, patches, clientIdsMap) {
|
|
1245
|
-
const result = {};
|
|
1246
|
-
(0, utils_1.objectKeys)(updatedDb.tables).forEach(entityName => {
|
|
1247
|
-
const table = updatedDb.tables[entityName];
|
|
1248
|
-
const schema = table.schema;
|
|
1249
|
-
let ids = I.Set();
|
|
1250
|
-
patches.forEach(patch => {
|
|
1251
|
-
let filteredPatch = (patch[entityName] || []).filter(entity => hasServerFields(entity, schema, clientIdsMap, updatedDb.tables));
|
|
1252
|
-
ids = ids.union(filteredPatch.map(e => table.getId(e)));
|
|
1253
|
-
});
|
|
1254
|
-
const entities = ids
|
|
1255
|
-
.map(id => table.byId(id))
|
|
1256
|
-
.filter(e => !isClientOnly(e, schema))
|
|
1257
|
-
.toArray();
|
|
1258
|
-
if (entities.length > 0) {
|
|
1259
|
-
result[entityName] = entities;
|
|
1260
|
-
}
|
|
1261
|
-
});
|
|
1262
|
-
return result;
|
|
1263
|
-
}
|
|
1264
|
-
exports.makeCumulativePatch = makeCumulativePatch;
|
|
1265
|
-
function isClientOnly(entity, schema) {
|
|
1266
|
-
if (typeof schema.isClientOnly === 'function') {
|
|
1267
|
-
return schema.isClientOnly(entity);
|
|
1268
|
-
}
|
|
1269
|
-
return !!schema.isClientOnly;
|
|
1270
|
-
}
|
|
1271
|
-
exports.isClientOnly = isClientOnly;
|
|
1272
|
-
function getId(entity, schema) {
|
|
1273
|
-
if (typeof schema.primaryKey == 'string') {
|
|
1274
|
-
return entity[schema.primaryKey];
|
|
1275
|
-
}
|
|
1276
|
-
const result = [];
|
|
1277
|
-
(0, lodash_foreach_1.default)((0, lodash_pick_1.default)(entity, schema.primaryKey), (value) => {
|
|
1278
|
-
result.push(value);
|
|
1279
|
-
});
|
|
1280
|
-
return result;
|
|
1281
|
-
}
|
|
1282
|
-
exports.getId = getId;
|
|
1283
|
-
function isNew(entity, schema, clientIdsMap) {
|
|
1284
|
-
const id = getId(entity, schema);
|
|
1285
|
-
return !clientIdsMap.clientToServer(id);
|
|
1286
|
-
}
|
|
1287
|
-
exports.isNew = isNew;
|
|
1288
|
-
function hasServerFields(entity, schema, clientIdsMap, tables) {
|
|
1289
|
-
const fullEntity = Object.assign(Object.assign({}, tables[schema.tableName].byId(entity.id) || {}), entity);
|
|
1290
|
-
if (isClientOnly(fullEntity, schema)) {
|
|
1291
|
-
return false;
|
|
1292
|
-
}
|
|
1293
|
-
return (0, utils_1.objectKeys)(entity).some(fieldName => {
|
|
1294
|
-
const fieldSchema = schema.fields[fieldName];
|
|
1295
|
-
return !fieldSchema || !(fieldSchema.isClientOnly || fieldSchema.isReadOnly || (fieldSchema.isGenerated && !isNew(entity, schema, clientIdsMap)));
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1298
|
-
exports.hasServerFields = hasServerFields;
|
|
1299
|
-
function getParentEntities(patch, tables) {
|
|
1300
|
-
const dependenciesPatch = {};
|
|
1301
|
-
(0, utils_1.objectKeys)(patch).forEach(key => {
|
|
1302
|
-
const table = tables[key];
|
|
1303
|
-
const tableSchema = table.schema;
|
|
1304
|
-
const fields = (0, lodash_filter_1.default)((0, lodash_map_1.default)(tableSchema.fields, (field, name) => (Object.assign(Object.assign({}, field), { name }))), field => field.fk && field.fk.relationType == types_1.DbRelationType.Aggregation);
|
|
1305
|
-
const tablePatch = patch[key];
|
|
1306
|
-
fields.forEach(field => {
|
|
1307
|
-
const { fk: { tableName }, name } = field;
|
|
1308
|
-
tablePatch.forEach(entity => {
|
|
1309
|
-
const actualTableName = (typeof tableName == 'function' ? tableName(entity) : tableName);
|
|
1310
|
-
const dbEntity = table.byId(table.getId(entity));
|
|
1311
|
-
const parentEntity = tables[actualTableName].byId(dbEntity[name]);
|
|
1312
|
-
const idFields = (0, lodash_pick_1.default)(parentEntity, tables[actualTableName].schema.primaryKey);
|
|
1313
|
-
if (parentEntity) {
|
|
1314
|
-
dependenciesPatch[actualTableName] = dependenciesPatch[actualTableName] || [];
|
|
1315
|
-
dependenciesPatch[actualTableName].push(idFields);
|
|
1316
|
-
}
|
|
1317
|
-
});
|
|
1318
|
-
});
|
|
1319
|
-
});
|
|
1320
|
-
return dependenciesPatch;
|
|
1321
|
-
}
|
|
1322
|
-
exports.getParentEntities = getParentEntities;
|
|
1323
|
-
function getEntityParentDependencies(entity, entityName, tables) {
|
|
1324
|
-
let patch = { [entityName]: [entity] };
|
|
1325
|
-
const resultPatches = [];
|
|
1326
|
-
while (true) {
|
|
1327
|
-
let parents = getParentEntities(patch, tables);
|
|
1328
|
-
if (isPatchBlank(parents)) {
|
|
1329
|
-
break;
|
|
1330
|
-
}
|
|
1331
|
-
resultPatches.push(parents);
|
|
1332
|
-
patch = parents;
|
|
1333
|
-
}
|
|
1334
|
-
let result = unionPatches([patch, ...resultPatches]);
|
|
1335
|
-
result = mergeEntityPatches(tables, result);
|
|
1336
|
-
return result;
|
|
1337
|
-
}
|
|
1338
|
-
exports.getEntityParentDependencies = getEntityParentDependencies;
|
|
1150
|
+
|
|
1151
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1152
|
+
exports.getEntityParentDependencies = exports.getParentEntities = exports.hasServerFields = exports.isNew = exports.getId = exports.isClientOnly = exports.makeCumulativePatch = exports.isPatchBlank = exports.flattenResponse = exports.mergeEntityPatches = exports.unionPatches = void 0;
|
|
1153
|
+
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
1154
|
+
const types_1 = __webpack_require__(/*! ./types */ "./src/types.ts");
|
|
1155
|
+
const I = (0, tslib_1.__importStar)(__webpack_require__(/*! immutable */ "immutable"));
|
|
1156
|
+
const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
1157
|
+
const lodash_groupby_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.groupby */ "lodash.groupby"));
|
|
1158
|
+
const lodash_map_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.map */ "lodash.map"));
|
|
1159
|
+
const lodash_merge_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.merge */ "lodash.merge"));
|
|
1160
|
+
const lodash_countby_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.countby */ "lodash.countby"));
|
|
1161
|
+
const lodash_filter_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.filter */ "lodash.filter"));
|
|
1162
|
+
const lodash_foreach_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.foreach */ "lodash.foreach"));
|
|
1163
|
+
const lodash_pick_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.pick */ "lodash.pick"));
|
|
1164
|
+
function unionPatches(patches) {
|
|
1165
|
+
const result = {};
|
|
1166
|
+
patches.forEach(patch => {
|
|
1167
|
+
(0, utils_1.objectKeys)(patch).forEach(tableName => {
|
|
1168
|
+
result[tableName] = result[tableName] ? [...result[tableName], ...patch[tableName]] : patch[tableName];
|
|
1169
|
+
});
|
|
1170
|
+
});
|
|
1171
|
+
return result;
|
|
1172
|
+
}
|
|
1173
|
+
exports.unionPatches = unionPatches;
|
|
1174
|
+
function mergeEntityPatches(tables, patch) {
|
|
1175
|
+
const result = {};
|
|
1176
|
+
(0, utils_1.objectKeys)(patch).forEach(key => {
|
|
1177
|
+
const table = tables[key];
|
|
1178
|
+
const groupedEntities = (0, lodash_groupby_1.default)(patch[key], item => table.getId(item));
|
|
1179
|
+
const tablePatch = (0, lodash_map_1.default)(groupedEntities, entities => {
|
|
1180
|
+
const result = (0, lodash_merge_1.default)({}, ...entities);
|
|
1181
|
+
if (true) {
|
|
1182
|
+
const props = (0, utils_1.objectKeys)(result).filter(prop => Object.keys((0, lodash_countby_1.default)(entities.filter(entity => entity[prop] !== undefined), entity => entity[prop])).length > 1);
|
|
1183
|
+
if (props.length) {
|
|
1184
|
+
console.warn('mergeEntityPatches: Different property values found for the same entity.\nProperty names:\n', props, '\nEntities:\n', ...entities);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
return result;
|
|
1188
|
+
});
|
|
1189
|
+
result[key] = tablePatch;
|
|
1190
|
+
});
|
|
1191
|
+
return result;
|
|
1192
|
+
}
|
|
1193
|
+
exports.mergeEntityPatches = mergeEntityPatches;
|
|
1194
|
+
function flattenResponseHelper(result, object, tablesByTypeName) {
|
|
1195
|
+
if (Array.isArray(object)) {
|
|
1196
|
+
object.forEach(item => flattenResponseHelper(result, item, tablesByTypeName));
|
|
1197
|
+
}
|
|
1198
|
+
else if (typeof object === 'object') {
|
|
1199
|
+
const clone = {};
|
|
1200
|
+
let tableKey;
|
|
1201
|
+
let fields = {};
|
|
1202
|
+
if ('__typename' in object) {
|
|
1203
|
+
const table = tablesByTypeName[object.__typename];
|
|
1204
|
+
if (table) {
|
|
1205
|
+
tableKey = table.schema.tableName;
|
|
1206
|
+
fields = table.schema.fields;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
for (let key in object) {
|
|
1210
|
+
const value = object[key];
|
|
1211
|
+
const field = fields[key];
|
|
1212
|
+
if ((field && !!field.disableFlatten)
|
|
1213
|
+
|| typeof value !== 'object'
|
|
1214
|
+
|| value === null
|
|
1215
|
+
|| (Array.isArray(value) && value.every(item => typeof item !== 'object'))) {
|
|
1216
|
+
clone[key] = value;
|
|
1217
|
+
}
|
|
1218
|
+
else {
|
|
1219
|
+
flattenResponseHelper(result, value, tablesByTypeName);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (tableKey) {
|
|
1223
|
+
result[tableKey].push(clone);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
function flattenResponse(response, tables) {
|
|
1228
|
+
const result = {};
|
|
1229
|
+
let tablesByTypeName = {};
|
|
1230
|
+
Object.values(tables).forEach(table => tablesByTypeName[table.schema.typeName] = table);
|
|
1231
|
+
(0, utils_1.objectKeys)(tables).forEach(tableName => tablesByTypeName);
|
|
1232
|
+
(0, utils_1.objectKeys)(tables).forEach(key => result[key] = []);
|
|
1233
|
+
flattenResponseHelper(result, response, tablesByTypeName);
|
|
1234
|
+
return result;
|
|
1235
|
+
}
|
|
1236
|
+
exports.flattenResponse = flattenResponse;
|
|
1237
|
+
function isPatchBlank(patch) {
|
|
1238
|
+
return !(0, utils_1.objectKeys)(patch).some(key => patch[key].length > 0);
|
|
1239
|
+
}
|
|
1240
|
+
exports.isPatchBlank = isPatchBlank;
|
|
1241
|
+
/**
|
|
1242
|
+
* Given several patches, creates a new single patch with all fields from all entities in all patches.
|
|
1243
|
+
*/
|
|
1244
|
+
function makeCumulativePatch(updatedDb, patches, clientIdsMap) {
|
|
1245
|
+
const result = {};
|
|
1246
|
+
(0, utils_1.objectKeys)(updatedDb.tables).forEach(entityName => {
|
|
1247
|
+
const table = updatedDb.tables[entityName];
|
|
1248
|
+
const schema = table.schema;
|
|
1249
|
+
let ids = I.Set();
|
|
1250
|
+
patches.forEach(patch => {
|
|
1251
|
+
let filteredPatch = (patch[entityName] || []).filter(entity => hasServerFields(entity, schema, clientIdsMap, updatedDb.tables));
|
|
1252
|
+
ids = ids.union(filteredPatch.map(e => table.getId(e)));
|
|
1253
|
+
});
|
|
1254
|
+
const entities = ids
|
|
1255
|
+
.map(id => table.byId(id))
|
|
1256
|
+
.filter(e => !isClientOnly(e, schema))
|
|
1257
|
+
.toArray();
|
|
1258
|
+
if (entities.length > 0) {
|
|
1259
|
+
result[entityName] = entities;
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
return result;
|
|
1263
|
+
}
|
|
1264
|
+
exports.makeCumulativePatch = makeCumulativePatch;
|
|
1265
|
+
function isClientOnly(entity, schema) {
|
|
1266
|
+
if (typeof schema.isClientOnly === 'function') {
|
|
1267
|
+
return schema.isClientOnly(entity);
|
|
1268
|
+
}
|
|
1269
|
+
return !!schema.isClientOnly;
|
|
1270
|
+
}
|
|
1271
|
+
exports.isClientOnly = isClientOnly;
|
|
1272
|
+
function getId(entity, schema) {
|
|
1273
|
+
if (typeof schema.primaryKey == 'string') {
|
|
1274
|
+
return entity[schema.primaryKey];
|
|
1275
|
+
}
|
|
1276
|
+
const result = [];
|
|
1277
|
+
(0, lodash_foreach_1.default)((0, lodash_pick_1.default)(entity, schema.primaryKey), (value) => {
|
|
1278
|
+
result.push(value);
|
|
1279
|
+
});
|
|
1280
|
+
return result;
|
|
1281
|
+
}
|
|
1282
|
+
exports.getId = getId;
|
|
1283
|
+
function isNew(entity, schema, clientIdsMap) {
|
|
1284
|
+
const id = getId(entity, schema);
|
|
1285
|
+
return !clientIdsMap.clientToServer(id);
|
|
1286
|
+
}
|
|
1287
|
+
exports.isNew = isNew;
|
|
1288
|
+
function hasServerFields(entity, schema, clientIdsMap, tables) {
|
|
1289
|
+
const fullEntity = Object.assign(Object.assign({}, tables[schema.tableName].byId(entity.id) || {}), entity);
|
|
1290
|
+
if (isClientOnly(fullEntity, schema)) {
|
|
1291
|
+
return false;
|
|
1292
|
+
}
|
|
1293
|
+
return (0, utils_1.objectKeys)(entity).some(fieldName => {
|
|
1294
|
+
const fieldSchema = schema.fields[fieldName];
|
|
1295
|
+
return !fieldSchema || !(fieldSchema.isClientOnly || fieldSchema.isReadOnly || (fieldSchema.isGenerated && !isNew(entity, schema, clientIdsMap)));
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
exports.hasServerFields = hasServerFields;
|
|
1299
|
+
function getParentEntities(patch, tables) {
|
|
1300
|
+
const dependenciesPatch = {};
|
|
1301
|
+
(0, utils_1.objectKeys)(patch).forEach(key => {
|
|
1302
|
+
const table = tables[key];
|
|
1303
|
+
const tableSchema = table.schema;
|
|
1304
|
+
const fields = (0, lodash_filter_1.default)((0, lodash_map_1.default)(tableSchema.fields, (field, name) => (Object.assign(Object.assign({}, field), { name }))), field => field.fk && field.fk.relationType == types_1.DbRelationType.Aggregation);
|
|
1305
|
+
const tablePatch = patch[key];
|
|
1306
|
+
fields.forEach(field => {
|
|
1307
|
+
const { fk: { tableName }, name } = field;
|
|
1308
|
+
tablePatch.forEach(entity => {
|
|
1309
|
+
const actualTableName = (typeof tableName == 'function' ? tableName(entity) : tableName);
|
|
1310
|
+
const dbEntity = table.byId(table.getId(entity));
|
|
1311
|
+
const parentEntity = tables[actualTableName].byId(dbEntity[name]);
|
|
1312
|
+
const idFields = (0, lodash_pick_1.default)(parentEntity, tables[actualTableName].schema.primaryKey);
|
|
1313
|
+
if (parentEntity) {
|
|
1314
|
+
dependenciesPatch[actualTableName] = dependenciesPatch[actualTableName] || [];
|
|
1315
|
+
dependenciesPatch[actualTableName].push(idFields);
|
|
1316
|
+
}
|
|
1317
|
+
});
|
|
1318
|
+
});
|
|
1319
|
+
});
|
|
1320
|
+
return dependenciesPatch;
|
|
1321
|
+
}
|
|
1322
|
+
exports.getParentEntities = getParentEntities;
|
|
1323
|
+
function getEntityParentDependencies(entity, entityName, tables) {
|
|
1324
|
+
let patch = { [entityName]: [entity] };
|
|
1325
|
+
const resultPatches = [];
|
|
1326
|
+
while (true) {
|
|
1327
|
+
let parents = getParentEntities(patch, tables);
|
|
1328
|
+
if (isPatchBlank(parents)) {
|
|
1329
|
+
break;
|
|
1330
|
+
}
|
|
1331
|
+
resultPatches.push(parents);
|
|
1332
|
+
patch = parents;
|
|
1333
|
+
}
|
|
1334
|
+
let result = unionPatches([patch, ...resultPatches]);
|
|
1335
|
+
result = mergeEntityPatches(tables, result);
|
|
1336
|
+
return result;
|
|
1337
|
+
}
|
|
1338
|
+
exports.getEntityParentDependencies = getEntityParentDependencies;
|
|
1339
1339
|
|
|
1340
1340
|
|
|
1341
1341
|
/***/ }),
|
|
@@ -1348,26 +1348,26 @@ exports.getEntityParentDependencies = getEntityParentDependencies;
|
|
|
1348
1348
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1349
1349
|
|
|
1350
1350
|
"use strict";
|
|
1351
|
-
|
|
1352
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1353
|
-
exports.useDbView = exports.useDbRef = exports.DbContext = void 0;
|
|
1354
|
-
const react_1 = __webpack_require__(/*! react */ "react");
|
|
1355
|
-
exports.DbContext = (0, react_1.createContext)(null);
|
|
1356
|
-
function useDbRef() {
|
|
1357
|
-
return (0, react_1.useContext)(exports.DbContext);
|
|
1358
|
-
}
|
|
1359
|
-
exports.useDbRef = useDbRef;
|
|
1360
|
-
function useDbView(fn, params, options) {
|
|
1361
|
-
const dbRef = useDbRef();
|
|
1362
|
-
const [state, setState] = (0, react_1.useState)();
|
|
1363
|
-
const subscription = (0, react_1.useMemo)(() => dbRef.subscribe(Object.assign({ compute: fn }, options), params, setState), []);
|
|
1364
|
-
subscription.update(params);
|
|
1365
|
-
(0, react_1.useEffect)(() => {
|
|
1366
|
-
return () => subscription.unsubscribe();
|
|
1367
|
-
}, []);
|
|
1368
|
-
return subscription.currentValue;
|
|
1369
|
-
}
|
|
1370
|
-
exports.useDbView = useDbView;
|
|
1351
|
+
|
|
1352
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1353
|
+
exports.useDbView = exports.useDbRef = exports.DbContext = void 0;
|
|
1354
|
+
const react_1 = __webpack_require__(/*! react */ "react");
|
|
1355
|
+
exports.DbContext = (0, react_1.createContext)(null);
|
|
1356
|
+
function useDbRef() {
|
|
1357
|
+
return (0, react_1.useContext)(exports.DbContext);
|
|
1358
|
+
}
|
|
1359
|
+
exports.useDbRef = useDbRef;
|
|
1360
|
+
function useDbView(fn, params, options) {
|
|
1361
|
+
const dbRef = useDbRef();
|
|
1362
|
+
const [state, setState] = (0, react_1.useState)();
|
|
1363
|
+
const subscription = (0, react_1.useMemo)(() => dbRef.subscribe(Object.assign({ compute: fn }, options), params, setState), []);
|
|
1364
|
+
subscription.update(params);
|
|
1365
|
+
(0, react_1.useEffect)(() => {
|
|
1366
|
+
return () => subscription.unsubscribe();
|
|
1367
|
+
}, []);
|
|
1368
|
+
return subscription.currentValue;
|
|
1369
|
+
}
|
|
1370
|
+
exports.useDbView = useDbView;
|
|
1371
1371
|
|
|
1372
1372
|
|
|
1373
1373
|
/***/ }),
|
|
@@ -1380,183 +1380,183 @@ exports.useDbView = useDbView;
|
|
|
1380
1380
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1381
1381
|
|
|
1382
1382
|
"use strict";
|
|
1383
|
-
|
|
1384
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1385
|
-
exports.TempIdMap = exports.isTempId = exports.getTempId = void 0;
|
|
1386
|
-
const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
1387
|
-
let lastTempId = -1;
|
|
1388
|
-
function getTempId() {
|
|
1389
|
-
return --lastTempId;
|
|
1390
|
-
}
|
|
1391
|
-
exports.getTempId = getTempId;
|
|
1392
|
-
function isTempId(val) {
|
|
1393
|
-
return typeof val === 'number' && val < 0;
|
|
1394
|
-
}
|
|
1395
|
-
exports.isTempId = isTempId;
|
|
1396
|
-
class TempIdMap {
|
|
1397
|
-
constructor(db) {
|
|
1398
|
-
this.db = db;
|
|
1399
|
-
this.serverToClientIds = new Map();
|
|
1400
|
-
this.clientToServerIds = new Map();
|
|
1401
|
-
this.clientToServer = (id) => this.clientToServerIds.get(id);
|
|
1402
|
-
this.serverToClient = (tableName, id) => this.getServerToClientMap(tableName).get(id);
|
|
1403
|
-
this.clientToServerRequest = (request) => {
|
|
1404
|
-
request = Object.assign({}, request);
|
|
1405
|
-
if (request.filter) {
|
|
1406
|
-
request.filter = this.clientToServerDataFilter(request.filter);
|
|
1407
|
-
}
|
|
1408
|
-
return request;
|
|
1409
|
-
};
|
|
1410
|
-
this.clientToServerDataFilter = (filter) => {
|
|
1411
|
-
filter = Object.assign({}, filter);
|
|
1412
|
-
const keys = Object.keys(filter);
|
|
1413
|
-
// I hope that we can rely that there won't be any negative numbers, which are not IDs, in queries.
|
|
1414
|
-
// We need to invent better heuristics or a schema otherwise.
|
|
1415
|
-
const clientToServer = (id) => {
|
|
1416
|
-
const found = this.clientToServer(id);
|
|
1417
|
-
if (found) {
|
|
1418
|
-
return found;
|
|
1419
|
-
}
|
|
1420
|
-
else {
|
|
1421
|
-
return id;
|
|
1422
|
-
}
|
|
1423
|
-
};
|
|
1424
|
-
for (let n = 0; n < keys.length; n++) {
|
|
1425
|
-
const key = keys[n];
|
|
1426
|
-
let condition = filter[key];
|
|
1427
|
-
if (condition != null && typeof condition === "object") {
|
|
1428
|
-
condition = Object.assign({}, condition);
|
|
1429
|
-
if ('in' in condition && Array.isArray(condition.in) && condition.in.length) {
|
|
1430
|
-
condition.in = condition.in.map(clientToServer);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
else {
|
|
1434
|
-
condition = clientToServer(condition);
|
|
1435
|
-
}
|
|
1436
|
-
filter[key] = condition;
|
|
1437
|
-
}
|
|
1438
|
-
return filter;
|
|
1439
|
-
};
|
|
1440
|
-
}
|
|
1441
|
-
copyPatch(patch) {
|
|
1442
|
-
const newPatch = Object.assign({}, patch);
|
|
1443
|
-
(0, utils_1.objectKeys)(patch).forEach(tableName => {
|
|
1444
|
-
let tablePatch = patch[tableName].map(entityPatch => (Object.assign({}, entityPatch)));
|
|
1445
|
-
newPatch[tableName] = tablePatch;
|
|
1446
|
-
});
|
|
1447
|
-
return newPatch;
|
|
1448
|
-
}
|
|
1449
|
-
// Iterate thru every field in every patch for every table, matching certain field criteria. The patch is mutated!
|
|
1450
|
-
mapFields(patch, filter, callback) {
|
|
1451
|
-
(0, utils_1.objectKeys)(patch).forEach(tableName => {
|
|
1452
|
-
const schema = this.db.tables[tableName].schema;
|
|
1453
|
-
const fields = (0, utils_1.objectKeys)(schema.fields || [])
|
|
1454
|
-
.map(fieldName => {
|
|
1455
|
-
const fieldSchema = schema.fields[fieldName];
|
|
1456
|
-
if (filter(fieldSchema, fieldName)) {
|
|
1457
|
-
return { name: fieldName, schema: fieldSchema };
|
|
1458
|
-
}
|
|
1459
|
-
})
|
|
1460
|
-
.filter(f => !!f);
|
|
1461
|
-
if (!fields.length) {
|
|
1462
|
-
return;
|
|
1463
|
-
}
|
|
1464
|
-
patch[tableName].forEach(entityPatch => {
|
|
1465
|
-
for (let n = 0; n < fields.length; n++) {
|
|
1466
|
-
const field = fields[n];
|
|
1467
|
-
const fieldValue = entityPatch[field.name];
|
|
1468
|
-
callback(field.name, field.schema, fieldValue, entityPatch, tableName);
|
|
1469
|
-
}
|
|
1470
|
-
});
|
|
1471
|
-
});
|
|
1472
|
-
}
|
|
1473
|
-
getServerToClientMap(tableName) {
|
|
1474
|
-
const existing = this.serverToClientIds.get(tableName);
|
|
1475
|
-
if (existing) {
|
|
1476
|
-
return existing;
|
|
1477
|
-
}
|
|
1478
|
-
else {
|
|
1479
|
-
const newMap = new Map();
|
|
1480
|
-
this.serverToClientIds.set(tableName, newMap);
|
|
1481
|
-
return newMap;
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
serverToClientPatch(patch) {
|
|
1485
|
-
patch = this.copyPatch(patch);
|
|
1486
|
-
// We map PK in a separate pass, as we need to remap all PKs before re-mapping FKs
|
|
1487
|
-
this.mapFields(patch, f => f.isGenerated, (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1488
|
-
let clientId = this.getServerToClientMap(tableName).get(fieldValue);
|
|
1489
|
-
if (!clientId) {
|
|
1490
|
-
clientId = getTempId();
|
|
1491
|
-
this.clientToServerIds.set(clientId, fieldValue);
|
|
1492
|
-
this.getServerToClientMap(tableName).set(fieldValue, clientId);
|
|
1493
|
-
}
|
|
1494
|
-
entityPatch[fieldName] = clientId;
|
|
1495
|
-
});
|
|
1496
|
-
this.mapFields(patch, f => !!(f.fk || (f.default != null) || f.toClient), (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1497
|
-
let isUndefined = typeof (fieldValue) === 'undefined';
|
|
1498
|
-
if (fieldSchema.default != null && fieldValue == null) {
|
|
1499
|
-
fieldValue = fieldSchema.default;
|
|
1500
|
-
isUndefined = false;
|
|
1501
|
-
}
|
|
1502
|
-
if (fieldSchema.fk && fieldValue != null) {
|
|
1503
|
-
const fkTableName = typeof fieldSchema.fk.tableName === 'function' ? fieldSchema.fk.tableName(entityPatch) : fieldSchema.fk.tableName;
|
|
1504
|
-
let clientId = this.getServerToClientMap(fkTableName).get(fieldValue);
|
|
1505
|
-
if (!clientId) {
|
|
1506
|
-
clientId = getTempId();
|
|
1507
|
-
this.clientToServerIds.set(clientId, fieldValue);
|
|
1508
|
-
this.getServerToClientMap(fkTableName).set(fieldValue, clientId);
|
|
1509
|
-
}
|
|
1510
|
-
fieldValue = clientId;
|
|
1511
|
-
}
|
|
1512
|
-
if (!isUndefined && fieldSchema.toClient) {
|
|
1513
|
-
fieldValue = fieldSchema.toClient(fieldValue);
|
|
1514
|
-
}
|
|
1515
|
-
if (!isUndefined) {
|
|
1516
|
-
entityPatch[fieldName] = fieldValue;
|
|
1517
|
-
}
|
|
1518
|
-
});
|
|
1519
|
-
return patch;
|
|
1520
|
-
}
|
|
1521
|
-
clientToServerPatch(patch) {
|
|
1522
|
-
patch = this.copyPatch(patch);
|
|
1523
|
-
this.mapFields(patch, f => f.isGenerated || !!f.fk, (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1524
|
-
if (fieldValue != null) {
|
|
1525
|
-
const existingId = this.clientToServerIds.get(fieldValue);
|
|
1526
|
-
if (existingId) {
|
|
1527
|
-
entityPatch[fieldName] = existingId;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
});
|
|
1531
|
-
this.mapFields(patch, f => !(f.isClientOnly || f.isReadOnly) && !!f.toServer, (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1532
|
-
entityPatch[fieldName] = fieldSchema.toServer(entityPatch[fieldName]);
|
|
1533
|
-
});
|
|
1534
|
-
this.mapFields(patch, meta => meta.isClientOnly || meta.isReadOnly, (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1535
|
-
delete entityPatch[fieldName];
|
|
1536
|
-
});
|
|
1537
|
-
return patch;
|
|
1538
|
-
}
|
|
1539
|
-
appendServerMapping(response) {
|
|
1540
|
-
(0, utils_1.objectKeys)(response.submit).forEach(tableName => {
|
|
1541
|
-
if (this.db.tables[tableName]) {
|
|
1542
|
-
const tablePatch = response.submit[tableName];
|
|
1543
|
-
if (tablePatch) {
|
|
1544
|
-
const serverToClientIds = this.getServerToClientMap(tableName);
|
|
1545
|
-
const table = this.db.tables[tableName];
|
|
1546
|
-
tablePatch.forEach(patch => {
|
|
1547
|
-
if (isTempId(patch.id)) {
|
|
1548
|
-
const clientId = patch.id;
|
|
1549
|
-
const serverId = table.getId(patch.payload);
|
|
1550
|
-
serverToClientIds.set(serverId, clientId);
|
|
1551
|
-
this.clientToServerIds.set(clientId, serverId);
|
|
1552
|
-
}
|
|
1553
|
-
});
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
});
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
exports.TempIdMap = TempIdMap;
|
|
1383
|
+
|
|
1384
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1385
|
+
exports.TempIdMap = exports.isTempId = exports.getTempId = void 0;
|
|
1386
|
+
const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
1387
|
+
let lastTempId = -1;
|
|
1388
|
+
function getTempId() {
|
|
1389
|
+
return --lastTempId;
|
|
1390
|
+
}
|
|
1391
|
+
exports.getTempId = getTempId;
|
|
1392
|
+
function isTempId(val) {
|
|
1393
|
+
return typeof val === 'number' && val < 0;
|
|
1394
|
+
}
|
|
1395
|
+
exports.isTempId = isTempId;
|
|
1396
|
+
class TempIdMap {
|
|
1397
|
+
constructor(db) {
|
|
1398
|
+
this.db = db;
|
|
1399
|
+
this.serverToClientIds = new Map();
|
|
1400
|
+
this.clientToServerIds = new Map();
|
|
1401
|
+
this.clientToServer = (id) => this.clientToServerIds.get(id);
|
|
1402
|
+
this.serverToClient = (tableName, id) => this.getServerToClientMap(tableName).get(id);
|
|
1403
|
+
this.clientToServerRequest = (request) => {
|
|
1404
|
+
request = Object.assign({}, request);
|
|
1405
|
+
if (request.filter) {
|
|
1406
|
+
request.filter = this.clientToServerDataFilter(request.filter);
|
|
1407
|
+
}
|
|
1408
|
+
return request;
|
|
1409
|
+
};
|
|
1410
|
+
this.clientToServerDataFilter = (filter) => {
|
|
1411
|
+
filter = Object.assign({}, filter);
|
|
1412
|
+
const keys = Object.keys(filter);
|
|
1413
|
+
// I hope that we can rely that there won't be any negative numbers, which are not IDs, in queries.
|
|
1414
|
+
// We need to invent better heuristics or a schema otherwise.
|
|
1415
|
+
const clientToServer = (id) => {
|
|
1416
|
+
const found = this.clientToServer(id);
|
|
1417
|
+
if (found) {
|
|
1418
|
+
return found;
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
return id;
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
for (let n = 0; n < keys.length; n++) {
|
|
1425
|
+
const key = keys[n];
|
|
1426
|
+
let condition = filter[key];
|
|
1427
|
+
if (condition != null && typeof condition === "object") {
|
|
1428
|
+
condition = Object.assign({}, condition);
|
|
1429
|
+
if ('in' in condition && Array.isArray(condition.in) && condition.in.length) {
|
|
1430
|
+
condition.in = condition.in.map(clientToServer);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
else {
|
|
1434
|
+
condition = clientToServer(condition);
|
|
1435
|
+
}
|
|
1436
|
+
filter[key] = condition;
|
|
1437
|
+
}
|
|
1438
|
+
return filter;
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
copyPatch(patch) {
|
|
1442
|
+
const newPatch = Object.assign({}, patch);
|
|
1443
|
+
(0, utils_1.objectKeys)(patch).forEach(tableName => {
|
|
1444
|
+
let tablePatch = patch[tableName].map(entityPatch => (Object.assign({}, entityPatch)));
|
|
1445
|
+
newPatch[tableName] = tablePatch;
|
|
1446
|
+
});
|
|
1447
|
+
return newPatch;
|
|
1448
|
+
}
|
|
1449
|
+
// Iterate thru every field in every patch for every table, matching certain field criteria. The patch is mutated!
|
|
1450
|
+
mapFields(patch, filter, callback) {
|
|
1451
|
+
(0, utils_1.objectKeys)(patch).forEach(tableName => {
|
|
1452
|
+
const schema = this.db.tables[tableName].schema;
|
|
1453
|
+
const fields = (0, utils_1.objectKeys)(schema.fields || [])
|
|
1454
|
+
.map(fieldName => {
|
|
1455
|
+
const fieldSchema = schema.fields[fieldName];
|
|
1456
|
+
if (filter(fieldSchema, fieldName)) {
|
|
1457
|
+
return { name: fieldName, schema: fieldSchema };
|
|
1458
|
+
}
|
|
1459
|
+
})
|
|
1460
|
+
.filter(f => !!f);
|
|
1461
|
+
if (!fields.length) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
patch[tableName].forEach(entityPatch => {
|
|
1465
|
+
for (let n = 0; n < fields.length; n++) {
|
|
1466
|
+
const field = fields[n];
|
|
1467
|
+
const fieldValue = entityPatch[field.name];
|
|
1468
|
+
callback(field.name, field.schema, fieldValue, entityPatch, tableName);
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
getServerToClientMap(tableName) {
|
|
1474
|
+
const existing = this.serverToClientIds.get(tableName);
|
|
1475
|
+
if (existing) {
|
|
1476
|
+
return existing;
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
const newMap = new Map();
|
|
1480
|
+
this.serverToClientIds.set(tableName, newMap);
|
|
1481
|
+
return newMap;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
serverToClientPatch(patch) {
|
|
1485
|
+
patch = this.copyPatch(patch);
|
|
1486
|
+
// We map PK in a separate pass, as we need to remap all PKs before re-mapping FKs
|
|
1487
|
+
this.mapFields(patch, f => f.isGenerated, (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1488
|
+
let clientId = this.getServerToClientMap(tableName).get(fieldValue);
|
|
1489
|
+
if (!clientId) {
|
|
1490
|
+
clientId = getTempId();
|
|
1491
|
+
this.clientToServerIds.set(clientId, fieldValue);
|
|
1492
|
+
this.getServerToClientMap(tableName).set(fieldValue, clientId);
|
|
1493
|
+
}
|
|
1494
|
+
entityPatch[fieldName] = clientId;
|
|
1495
|
+
});
|
|
1496
|
+
this.mapFields(patch, f => !!(f.fk || (f.default != null) || f.toClient), (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1497
|
+
let isUndefined = typeof (fieldValue) === 'undefined';
|
|
1498
|
+
if (fieldSchema.default != null && fieldValue == null) {
|
|
1499
|
+
fieldValue = fieldSchema.default;
|
|
1500
|
+
isUndefined = false;
|
|
1501
|
+
}
|
|
1502
|
+
if (fieldSchema.fk && fieldValue != null) {
|
|
1503
|
+
const fkTableName = typeof fieldSchema.fk.tableName === 'function' ? fieldSchema.fk.tableName(entityPatch) : fieldSchema.fk.tableName;
|
|
1504
|
+
let clientId = this.getServerToClientMap(fkTableName).get(fieldValue);
|
|
1505
|
+
if (!clientId) {
|
|
1506
|
+
clientId = getTempId();
|
|
1507
|
+
this.clientToServerIds.set(clientId, fieldValue);
|
|
1508
|
+
this.getServerToClientMap(fkTableName).set(fieldValue, clientId);
|
|
1509
|
+
}
|
|
1510
|
+
fieldValue = clientId;
|
|
1511
|
+
}
|
|
1512
|
+
if (!isUndefined && fieldSchema.toClient) {
|
|
1513
|
+
fieldValue = fieldSchema.toClient(fieldValue);
|
|
1514
|
+
}
|
|
1515
|
+
if (!isUndefined) {
|
|
1516
|
+
entityPatch[fieldName] = fieldValue;
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
return patch;
|
|
1520
|
+
}
|
|
1521
|
+
clientToServerPatch(patch) {
|
|
1522
|
+
patch = this.copyPatch(patch);
|
|
1523
|
+
this.mapFields(patch, f => f.isGenerated || !!f.fk, (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1524
|
+
if (fieldValue != null) {
|
|
1525
|
+
const existingId = this.clientToServerIds.get(fieldValue);
|
|
1526
|
+
if (existingId) {
|
|
1527
|
+
entityPatch[fieldName] = existingId;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
});
|
|
1531
|
+
this.mapFields(patch, f => !(f.isClientOnly || f.isReadOnly) && !!f.toServer, (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1532
|
+
entityPatch[fieldName] = fieldSchema.toServer(entityPatch[fieldName]);
|
|
1533
|
+
});
|
|
1534
|
+
this.mapFields(patch, meta => meta.isClientOnly || meta.isReadOnly, (fieldName, fieldSchema, fieldValue, entityPatch, tableName) => {
|
|
1535
|
+
delete entityPatch[fieldName];
|
|
1536
|
+
});
|
|
1537
|
+
return patch;
|
|
1538
|
+
}
|
|
1539
|
+
appendServerMapping(response) {
|
|
1540
|
+
(0, utils_1.objectKeys)(response.submit).forEach(tableName => {
|
|
1541
|
+
if (this.db.tables[tableName]) {
|
|
1542
|
+
const tablePatch = response.submit[tableName];
|
|
1543
|
+
if (tablePatch) {
|
|
1544
|
+
const serverToClientIds = this.getServerToClientMap(tableName);
|
|
1545
|
+
const table = this.db.tables[tableName];
|
|
1546
|
+
tablePatch.forEach(patch => {
|
|
1547
|
+
if (isTempId(patch.id)) {
|
|
1548
|
+
const clientId = patch.id;
|
|
1549
|
+
const serverId = table.getId(patch.payload);
|
|
1550
|
+
serverToClientIds.set(serverId, clientId);
|
|
1551
|
+
this.clientToServerIds.set(clientId, serverId);
|
|
1552
|
+
}
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
exports.TempIdMap = TempIdMap;
|
|
1560
1560
|
|
|
1561
1561
|
|
|
1562
1562
|
/***/ }),
|
|
@@ -1569,14 +1569,14 @@ exports.TempIdMap = TempIdMap;
|
|
|
1569
1569
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1570
1570
|
|
|
1571
1571
|
"use strict";
|
|
1572
|
-
|
|
1573
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1574
|
-
exports.DbRelationType = void 0;
|
|
1575
|
-
var DbRelationType;
|
|
1576
|
-
(function (DbRelationType) {
|
|
1577
|
-
DbRelationType[DbRelationType["Aggregation"] = 0] = "Aggregation";
|
|
1578
|
-
DbRelationType[DbRelationType["Association"] = 1] = "Association";
|
|
1579
|
-
})(DbRelationType = exports.DbRelationType || (exports.DbRelationType = {}));
|
|
1572
|
+
|
|
1573
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1574
|
+
exports.DbRelationType = void 0;
|
|
1575
|
+
var DbRelationType;
|
|
1576
|
+
(function (DbRelationType) {
|
|
1577
|
+
DbRelationType[DbRelationType["Aggregation"] = 0] = "Aggregation";
|
|
1578
|
+
DbRelationType[DbRelationType["Association"] = 1] = "Association";
|
|
1579
|
+
})(DbRelationType = exports.DbRelationType || (exports.DbRelationType = {}));
|
|
1580
1580
|
|
|
1581
1581
|
|
|
1582
1582
|
/***/ }),
|
|
@@ -1589,43 +1589,43 @@ var DbRelationType;
|
|
|
1589
1589
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1590
1590
|
|
|
1591
1591
|
"use strict";
|
|
1592
|
-
|
|
1593
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1594
|
-
exports.difference = exports.defaultCompareViewDependencies = exports.objectKeys = void 0;
|
|
1595
|
-
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
1596
|
-
const shallowequal_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! shallowequal */ "shallowequal"));
|
|
1597
|
-
const lodash_zip_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.zip */ "lodash.zip"));
|
|
1598
|
-
const lodash_transform_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.transform */ "lodash.transform"));
|
|
1599
|
-
const lodash_isequal_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.isequal */ "lodash.isequal"));
|
|
1600
|
-
// TBD: New typescript would contain better typing for this. Remove this hack after update.
|
|
1601
|
-
exports.objectKeys = Object.keys;
|
|
1602
|
-
function defaultCompareViewDependencies(prev, next) {
|
|
1603
|
-
if (Array.isArray(prev) && Array.isArray(next)) {
|
|
1604
|
-
if (prev.length === next.length) {
|
|
1605
|
-
return (0, lodash_zip_1.default)(prev, next).every(([p, n]) => (0, shallowequal_1.default)(p, n));
|
|
1606
|
-
}
|
|
1607
|
-
return false;
|
|
1608
|
-
}
|
|
1609
|
-
return (0, shallowequal_1.default)(prev, next);
|
|
1610
|
-
}
|
|
1611
|
-
exports.defaultCompareViewDependencies = defaultCompareViewDependencies;
|
|
1612
|
-
/**
|
|
1613
|
-
* Deep diff between two object, using lodash
|
|
1614
|
-
* @param {Object} object Object compared
|
|
1615
|
-
* @param {Object} base Object to compare with
|
|
1616
|
-
* @return {Object} Return a new object who represent the diff
|
|
1617
|
-
*/
|
|
1618
|
-
function difference(object, base) {
|
|
1619
|
-
function changes(object, base) {
|
|
1620
|
-
return (0, lodash_transform_1.default)(object, function (result, value, key) {
|
|
1621
|
-
if (!(0, lodash_isequal_1.default)(value, base[key])) {
|
|
1622
|
-
result[key] = (typeof value === "object" && typeof base[key] === "object") ? changes(value, base[key]) : value;
|
|
1623
|
-
}
|
|
1624
|
-
});
|
|
1625
|
-
}
|
|
1626
|
-
return changes(object, base);
|
|
1627
|
-
}
|
|
1628
|
-
exports.difference = difference;
|
|
1592
|
+
|
|
1593
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1594
|
+
exports.difference = exports.defaultCompareViewDependencies = exports.objectKeys = void 0;
|
|
1595
|
+
const tslib_1 = __webpack_require__(/*! tslib */ "../node_modules/tslib/tslib.es6.js");
|
|
1596
|
+
const shallowequal_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! shallowequal */ "shallowequal"));
|
|
1597
|
+
const lodash_zip_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.zip */ "lodash.zip"));
|
|
1598
|
+
const lodash_transform_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.transform */ "lodash.transform"));
|
|
1599
|
+
const lodash_isequal_1 = (0, tslib_1.__importDefault)(__webpack_require__(/*! lodash.isequal */ "lodash.isequal"));
|
|
1600
|
+
// TBD: New typescript would contain better typing for this. Remove this hack after update.
|
|
1601
|
+
exports.objectKeys = Object.keys;
|
|
1602
|
+
function defaultCompareViewDependencies(prev, next) {
|
|
1603
|
+
if (Array.isArray(prev) && Array.isArray(next)) {
|
|
1604
|
+
if (prev.length === next.length) {
|
|
1605
|
+
return (0, lodash_zip_1.default)(prev, next).every(([p, n]) => (0, shallowequal_1.default)(p, n));
|
|
1606
|
+
}
|
|
1607
|
+
return false;
|
|
1608
|
+
}
|
|
1609
|
+
return (0, shallowequal_1.default)(prev, next);
|
|
1610
|
+
}
|
|
1611
|
+
exports.defaultCompareViewDependencies = defaultCompareViewDependencies;
|
|
1612
|
+
/**
|
|
1613
|
+
* Deep diff between two object, using lodash
|
|
1614
|
+
* @param {Object} object Object compared
|
|
1615
|
+
* @param {Object} base Object to compare with
|
|
1616
|
+
* @return {Object} Return a new object who represent the diff
|
|
1617
|
+
*/
|
|
1618
|
+
function difference(object, base) {
|
|
1619
|
+
function changes(object, base) {
|
|
1620
|
+
return (0, lodash_transform_1.default)(object, function (result, value, key) {
|
|
1621
|
+
if (!(0, lodash_isequal_1.default)(value, base[key])) {
|
|
1622
|
+
result[key] = (typeof value === "object" && typeof base[key] === "object") ? changes(value, base[key]) : value;
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
return changes(object, base);
|
|
1627
|
+
}
|
|
1628
|
+
exports.difference = difference;
|
|
1629
1629
|
|
|
1630
1630
|
|
|
1631
1631
|
/***/ }),
|
|
@@ -1637,7 +1637,7 @@ exports.difference = difference;
|
|
|
1637
1637
|
/*! no static exports found */
|
|
1638
1638
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1639
1639
|
|
|
1640
|
-
module.exports = __webpack_require__(/*!
|
|
1640
|
+
module.exports = __webpack_require__(/*! D:\dev\UUI-main\uui-db\index.tsx */"./index.tsx");
|
|
1641
1641
|
|
|
1642
1642
|
|
|
1643
1643
|
/***/ }),
|