@rljson/db 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/db.js CHANGED
@@ -1,14 +1,16 @@
1
- import { equals, merge } from "@rljson/json";
2
- import { timeId, createInsertHistoryTableCfg, Validate, BaseValidator, Route, validateInsert, isTimeId } from "@rljson/rljson";
3
- import { traverse } from "object-traversal";
4
1
  import { rmhsh, hsh, Hash, hip } from "@rljson/hash";
2
+ import { equals, merge } from "@rljson/json";
3
+ import { timeId, createInsertHistoryTableCfg, Validate, BaseValidator, Route, isTimeId } from "@rljson/rljson";
5
4
  import { IoMem } from "@rljson/io";
5
+ import { traverse } from "object-traversal";
6
6
  import { compileExpression } from "filtrex";
7
7
  class BaseController {
8
8
  constructor(_core, _tableKey) {
9
9
  this._core = _core;
10
10
  this._tableKey = _tableKey;
11
11
  }
12
+ _contentType;
13
+ _tableCfg;
12
14
  // ...........................................................................
13
15
  /**
14
16
  * Retrieves the current state of the table.
@@ -67,6 +69,28 @@ class BaseController {
67
69
  });
68
70
  return rows;
69
71
  }
72
+ // ...........................................................................
73
+ /**
74
+ * Gets the content type of the controller.
75
+ * @returns The content type managed by the controller.
76
+ */
77
+ /* v8 ignore next -- @preserve */
78
+ contentType() {
79
+ return this._contentType ?? "components";
80
+ }
81
+ // ...........................................................................
82
+ /**
83
+ * Gets the table configuration of the controller.
84
+ * @returns The table configuration managed by the controller.
85
+ */
86
+ tableCfg() {
87
+ if (!this._tableCfg) {
88
+ throw new Error(
89
+ `TableCfg for controller ${this._tableKey} is not initialized.`
90
+ );
91
+ }
92
+ return this._tableCfg;
93
+ }
70
94
  }
71
95
  class CakeController extends BaseController {
72
96
  constructor(_core, _tableKey, _refs) {
@@ -74,6 +98,7 @@ class CakeController extends BaseController {
74
98
  this._core = _core;
75
99
  this._tableKey = _tableKey;
76
100
  this._refs = _refs;
101
+ this._contentType = "cakes";
77
102
  }
78
103
  _table = null;
79
104
  _baseLayers = {};
@@ -88,7 +113,8 @@ class CakeController extends BaseController {
88
113
  if (this._table._type !== "cakes") {
89
114
  throw new Error(`Table ${this._tableKey} is not of type cakes.`);
90
115
  }
91
- if (this._refs && this._refs.base && this._refs.base.length > 0) {
116
+ this._tableCfg = await this._core.tableCfg(this._tableKey);
117
+ if (this._refs && this._refs.base && this._refs.base !== void 0 && this._refs.base.length > 0) {
92
118
  const {
93
119
  [this._tableKey]: { _data: baseCakes }
94
120
  } = await this._core.readRow(this._tableKey, this._refs.base);
@@ -99,11 +125,13 @@ class CakeController extends BaseController {
99
125
  this._baseLayers = rmhsh(baseCake.layers);
100
126
  } else {
101
127
  const cake = this._table._data[0];
102
- this._refs = {
103
- sliceIdsTable: cake.sliceIdsTable,
104
- sliceIdsRow: cake.sliceIdsRow
105
- };
106
- this._baseLayers = rmhsh(cake.layers);
128
+ if (!!cake) {
129
+ this._refs = {
130
+ sliceIdsTable: cake.sliceIdsTable,
131
+ sliceIdsRow: cake.sliceIdsRow
132
+ };
133
+ this._baseLayers = rmhsh(cake.layers);
134
+ }
107
135
  }
108
136
  }
109
137
  async getChildRefs(where, filter) {
@@ -131,7 +159,7 @@ class CakeController extends BaseController {
131
159
  if (this._refs?.base) delete this._refs.base;
132
160
  const normalizedValue = {};
133
161
  for (const [layerTable, layerRef] of Object.entries(
134
- value
162
+ value.layers
135
163
  )) {
136
164
  if (Array.isArray(layerRef) && layerRef.length > 1) {
137
165
  throw new Error(
@@ -141,6 +169,7 @@ class CakeController extends BaseController {
141
169
  normalizedValue[layerTable] = Array.isArray(layerRef) ? layerRef[0] : layerRef;
142
170
  }
143
171
  const cake = {
172
+ ...value,
144
173
  layers: { ...this._baseLayers, ...normalizedValue },
145
174
  ...refs || this._refs
146
175
  };
@@ -166,7 +195,7 @@ class CakeController extends BaseController {
166
195
  return Promise.resolve({});
167
196
  }
168
197
  }
169
- filterRow(row, key, value) {
198
+ async filterRow(row, key, value) {
170
199
  const cake = row;
171
200
  for (const [layerKey, layerRef] of Object.entries(cake.layers)) {
172
201
  if (layerKey === key && layerRef === value) {
@@ -182,8 +211,14 @@ class ComponentController extends BaseController {
182
211
  this._core = _core;
183
212
  this._tableKey = _tableKey;
184
213
  this._refs = _refs;
214
+ this._contentType = "components";
185
215
  }
186
- _tableCfg = null;
216
+ _allowedContentTypes = [
217
+ "components",
218
+ "edits",
219
+ "editHistory",
220
+ "multiEdits"
221
+ ];
187
222
  _resolvedColumns = null;
188
223
  _refTableKeyToColumnKeyMap = null;
189
224
  async init() {
@@ -192,7 +227,7 @@ class ComponentController extends BaseController {
192
227
  }
193
228
  const rljson = await this._core.dumpTable(this._tableKey);
194
229
  const table = rljson[this._tableKey];
195
- if (table._type !== "components") {
230
+ if (this._allowedContentTypes.indexOf(table._type) === -1) {
196
231
  throw new Error(`Table ${this._tableKey} is not of type components.`);
197
232
  }
198
233
  this._tableCfg = await this._core.tableCfg(this._tableKey);
@@ -210,38 +245,12 @@ class ComponentController extends BaseController {
210
245
  if (!!refs) {
211
246
  throw new Error(`Refs are not supported on ComponentController.`);
212
247
  }
213
- const values = [];
214
- const referencedValues = /* @__PURE__ */ new Map();
215
- for (const [k, v] of Object.entries(value)) {
216
- if (Array.isArray(v)) {
217
- for (const possibleRef of v) {
218
- if (typeof possibleRef === "object" && possibleRef.hasOwnProperty("_ref") && possibleRef.hasOwnProperty("_value")) {
219
- const ref = possibleRef._ref;
220
- const val = possibleRef._value;
221
- if (!referencedValues.has(ref))
222
- referencedValues.set(ref, { [k]: val });
223
- else {
224
- const existing = referencedValues.get(ref);
225
- referencedValues.set(ref, { ...{ [k]: val }, ...existing });
226
- }
227
- }
228
- }
229
- }
230
- }
231
- if (referencedValues.size > 0) {
232
- for (const refValue of referencedValues.values()) {
233
- values.push({ ...value, ...refValue });
234
- }
235
- } else {
236
- values.push(value);
237
- }
238
- const components = values;
239
- const results = [];
240
- for (const component of components) {
241
- delete component._somethingToInsert;
242
- const rlJson = { [this._tableKey]: { _data: [component] } };
243
- await this._core.import(rlJson);
244
- const result = {
248
+ const component = value;
249
+ delete component._somethingToInsert;
250
+ const rlJson = { [this._tableKey]: { _data: [component] } };
251
+ await this._core.import(rlJson);
252
+ return [
253
+ {
245
254
  //Ref to component
246
255
  [this._tableKey + "Ref"]: hsh(component)._hash,
247
256
  //Data from edit
@@ -249,10 +258,8 @@ class ComponentController extends BaseController {
249
258
  origin,
250
259
  //Unique id/timestamp
251
260
  timeId: timeId()
252
- };
253
- results.push(result);
254
- }
255
- return results;
261
+ }
262
+ ];
256
263
  }
257
264
  // ...........................................................................
258
265
  /**
@@ -287,6 +294,20 @@ class ComponentController extends BaseController {
287
294
  columnKey: propertyKey,
288
295
  ref: refItem
289
296
  });
297
+ continue;
298
+ }
299
+ if (typeof refItem === "object" && refItem !== null) {
300
+ const cakeReference = refItem;
301
+ childRefs.set(
302
+ `${childRefTableKey}|${propertyKey}|${cakeReference.ref}|${cakeReference.sliceIds?.join(",")}`,
303
+ {
304
+ tableKey: childRefTableKey,
305
+ columnKey: propertyKey,
306
+ ref: cakeReference.ref,
307
+ sliceIds: cakeReference.sliceIds
308
+ }
309
+ );
310
+ continue;
290
311
  }
291
312
  }
292
313
  continue;
@@ -319,7 +340,7 @@ class ComponentController extends BaseController {
319
340
  const column = Object.keys(refWhere)[0];
320
341
  const refValue = refWhere[column];
321
342
  for (const row of tableData) {
322
- if (this.filterRow(row, column, refValue)) {
343
+ if (await this.filterRow(row, column, refValue)) {
323
344
  consolidatedRows.set(row._hash, row);
324
345
  }
325
346
  }
@@ -518,7 +539,7 @@ class ComponentController extends BaseController {
518
539
  return this._core.readRows(table, where);
519
540
  }
520
541
  }
521
- filterRow(row, key, value) {
542
+ async filterRow(row, key, value) {
522
543
  for (const [propertyKey, propertyValue] of Object.entries(row)) {
523
544
  if (propertyKey === key && equals(propertyValue, value)) {
524
545
  return true;
@@ -533,12 +554,131 @@ class ComponentController extends BaseController {
533
554
  return false;
534
555
  }
535
556
  }
557
+ class SliceIdController extends BaseController {
558
+ constructor(_core, _tableKey, _refs) {
559
+ super(_core, _tableKey);
560
+ this._core = _core;
561
+ this._tableKey = _tableKey;
562
+ this._refs = _refs;
563
+ this._contentType = "sliceIds";
564
+ }
565
+ async init() {
566
+ if (this._tableKey.endsWith("SliceId") === false) {
567
+ throw new Error(
568
+ `Table ${this._tableKey} is not supported by SliceIdController.`
569
+ );
570
+ }
571
+ const rljson = await this._core.dumpTable(this._tableKey);
572
+ const table = rljson[this._tableKey];
573
+ if (table._type !== "sliceIds") {
574
+ throw new Error(`Table ${this._tableKey} is not of type sliceIds.`);
575
+ }
576
+ this._tableCfg = await this._core.tableCfg(this._tableKey);
577
+ if (this._refs && this._refs.base) {
578
+ const {
579
+ [this._tableKey]: { _data: SliceIds2 }
580
+ } = await this._core.readRow(this._tableKey, this._refs.base);
581
+ if (SliceIds2.length === 0) {
582
+ throw new Error(`Base sliceId ${this._refs.base} does not exist.`);
583
+ }
584
+ } else {
585
+ const sliceId = table._data[0];
586
+ if (!!sliceId) {
587
+ this._refs = {
588
+ base: sliceId.base
589
+ };
590
+ }
591
+ }
592
+ }
593
+ async insert(command, value, origin, refs) {
594
+ if (!command.startsWith("add") && !command.startsWith("remove")) {
595
+ throw new Error(
596
+ `Command ${command} is not supported by SliceIdController.`
597
+ );
598
+ }
599
+ const sliceIds = command.startsWith("add") === true ? {
600
+ add: value,
601
+ ...refs || this._refs
602
+ } : {
603
+ add: [],
604
+ remove: value,
605
+ ...refs || this._refs
606
+ };
607
+ const rlJson = { [this._tableKey]: { _data: [sliceIds] } };
608
+ await this._core.import(rlJson);
609
+ const result = {
610
+ //Ref to component
611
+ [this._tableKey + "Ref"]: hsh(sliceIds)._hash,
612
+ //Data from edit
613
+ route: "",
614
+ origin,
615
+ //Unique id/timestamp
616
+ timeId: timeId()
617
+ };
618
+ return [result];
619
+ }
620
+ async get(where, filter) {
621
+ if (typeof where === "string") {
622
+ return this._getByHash(where, filter);
623
+ } else {
624
+ return this._getByWhere(where, filter);
625
+ }
626
+ }
627
+ async resolveBaseSliceIds(sliceIds) {
628
+ const add = /* @__PURE__ */ new Set();
629
+ const remove = /* @__PURE__ */ new Set();
630
+ if (!!sliceIds.base) {
631
+ const baseSliceIds = await this.get(sliceIds.base);
632
+ if (!baseSliceIds[this._tableKey]?._data?.[0]) {
633
+ throw new Error(`Base sliceIds ${sliceIds.base} does not exist.`);
634
+ }
635
+ if (baseSliceIds[this._tableKey]._data.length > 1) {
636
+ throw new Error(
637
+ `Base sliceIds ${sliceIds.base} has more than one entry.`
638
+ );
639
+ }
640
+ const baseSliceId = baseSliceIds[this._tableKey]._data[0];
641
+ const resolvedBaseSliceIds = await this.resolveBaseSliceIds(baseSliceId);
642
+ for (const sliceId of resolvedBaseSliceIds.add) {
643
+ add.add(sliceId);
644
+ }
645
+ }
646
+ for (const sliceId of sliceIds.add) {
647
+ add.add(sliceId);
648
+ }
649
+ if (!!sliceIds.remove)
650
+ for (const sliceId of sliceIds.remove) {
651
+ remove.add(sliceId);
652
+ }
653
+ for (const sliceId of remove.values()) {
654
+ if (add.has(sliceId)) {
655
+ add.delete(sliceId);
656
+ }
657
+ }
658
+ return { add: Array.from(add) };
659
+ }
660
+ /* v8 ignore next -- @preserve */
661
+ async getChildRefs() {
662
+ return [];
663
+ }
664
+ async filterRow(row, _, value) {
665
+ const sliceIds = row;
666
+ const sliceId = value;
667
+ for (const sId of Object.values(sliceIds.add)) {
668
+ if (sliceId === sId) {
669
+ return true;
670
+ }
671
+ }
672
+ return false;
673
+ }
674
+ }
536
675
  class LayerController extends BaseController {
537
676
  constructor(_core, _tableKey, _refs) {
538
677
  super(_core, _tableKey);
539
678
  this._core = _core;
540
679
  this._tableKey = _tableKey;
541
680
  this._refs = _refs;
681
+ this._contentType = "layers";
542
682
  }
543
683
  async init() {
544
684
  if (this._tableKey.endsWith("Layer") === false) {
@@ -551,6 +691,7 @@ class LayerController extends BaseController {
551
691
  if (table._type !== "layers") {
552
692
  throw new Error(`Table ${this._tableKey} is not of type layers.`);
553
693
  }
694
+ this._tableCfg = await this._core.tableCfg(this._tableKey);
554
695
  if (this._refs && this._refs.base) {
555
696
  const {
556
697
  [this._tableKey]: { _data: baseLayers }
@@ -568,11 +709,13 @@ class LayerController extends BaseController {
568
709
  }
569
710
  } else {
570
711
  const layer = table._data[0];
571
- this._refs = {
572
- sliceIdsTable: layer.sliceIdsTable,
573
- sliceIdsTableRow: layer.sliceIdsTableRow,
574
- componentsTable: layer.componentsTable
575
- };
712
+ if (!!layer) {
713
+ this._refs = {
714
+ sliceIdsTable: layer.sliceIdsTable,
715
+ sliceIdsTableRow: layer.sliceIdsTableRow,
716
+ componentsTable: layer.componentsTable
717
+ };
718
+ }
576
719
  }
577
720
  }
578
721
  async insert(command, value, origin, refs) {
@@ -581,10 +724,9 @@ class LayerController extends BaseController {
581
724
  `Command ${command} is not supported by LayerController.`
582
725
  );
583
726
  }
727
+ const isAdd = command.startsWith("add");
584
728
  const normalizedValue = {};
585
- for (const [sliceId, compRef] of Object.entries(
586
- value
587
- )) {
729
+ for (const [sliceId, compRef] of isAdd ? Object.entries(value.add) : Object.entries(value.remove)) {
588
730
  if (Array.isArray(compRef) && compRef.length > 1) {
589
731
  throw new Error(
590
732
  `LayerController insert: Component ref for slice ${sliceId} cannot be an array of size > 1. No 1:n relations supported.`
@@ -592,13 +734,20 @@ class LayerController extends BaseController {
592
734
  }
593
735
  normalizedValue[sliceId] = Array.isArray(compRef) ? compRef[0] : compRef;
594
736
  }
595
- const layer = command.startsWith("add") === true ? {
596
- add: normalizedValue,
597
- ...refs || this._refs
737
+ const layer = isAdd ? {
738
+ ...value,
739
+ ...{
740
+ add: normalizedValue,
741
+ remove: {}
742
+ },
743
+ ...refs
598
744
  } : {
599
- add: {},
600
- remove: normalizedValue,
601
- ...refs || this._refs
745
+ ...value,
746
+ ...{
747
+ remove: normalizedValue,
748
+ add: {}
749
+ },
750
+ ...refs
602
751
  };
603
752
  const rlJson = { [this._tableKey]: { _data: [layer] } };
604
753
  await this._core.import(rlJson);
@@ -620,25 +769,108 @@ class LayerController extends BaseController {
620
769
  return this._getByWhere(where, filter);
621
770
  }
622
771
  }
772
+ async resolveBaseLayer(layer) {
773
+ const add = /* @__PURE__ */ new Map();
774
+ const sliceIds = /* @__PURE__ */ new Set();
775
+ if (!!layer.base) {
776
+ const baseLayer = await this.get(layer.base);
777
+ if (!baseLayer[this._tableKey]?._data?.[0]) {
778
+ throw new Error(`Base layer ${layer.base} does not exist.`);
779
+ }
780
+ if (baseLayer[this._tableKey]._data.length > 1) {
781
+ throw new Error(
782
+ `Base layer ${layer.base} resolving not possible. Not unique.`
783
+ );
784
+ }
785
+ const baseLayerData = rmhsh(baseLayer[this._tableKey]._data[0]);
786
+ const baseLayerResolved = await this.resolveBaseLayer(baseLayerData);
787
+ for (const [sliceId, compRef] of Object.entries(baseLayerResolved.add)) {
788
+ if (sliceId.startsWith("_")) continue;
789
+ add.set(sliceId, compRef);
790
+ }
791
+ for (const sliceId of baseLayerResolved.sliceIds) {
792
+ sliceIds.add(sliceId);
793
+ }
794
+ const baseLayerSliceIdsTable = baseLayerData.sliceIdsTable;
795
+ const baseLayerSliceIdsRow = baseLayerData.sliceIdsTableRow;
796
+ const {
797
+ [baseLayerSliceIdsTable]: { _data: baseLayerSliceIds }
798
+ } = await this._core.readRow(
799
+ baseLayerSliceIdsTable,
800
+ baseLayerSliceIdsRow
801
+ );
802
+ for (const sIds of baseLayerSliceIds) {
803
+ const sliceIdController = new SliceIdController(
804
+ this._core,
805
+ baseLayerSliceIdsTable
806
+ );
807
+ const resolvedSliceIds = await sliceIdController.resolveBaseSliceIds(
808
+ sIds
809
+ );
810
+ for (const sId of resolvedSliceIds.add) {
811
+ if (sId.startsWith("_")) continue;
812
+ sliceIds.add(sId);
813
+ }
814
+ }
815
+ }
816
+ const {
817
+ [layer.sliceIdsTable]: { _data: layerSliceIds }
818
+ } = await this._core.readRow(layer.sliceIdsTable, layer.sliceIdsTableRow);
819
+ if (!layerSliceIds || layerSliceIds.length === 0) {
820
+ throw new Error(
821
+ `Layer sliceIds ${layer.sliceIdsTableRow} does not exist.`
822
+ );
823
+ }
824
+ if (layerSliceIds.length > 1) {
825
+ throw new Error(
826
+ `Layer sliceIds ${layer.sliceIdsTableRow} has more than one entry.`
827
+ );
828
+ }
829
+ const layerSliceId = layerSliceIds[0];
830
+ for (const sId of layerSliceId.add) {
831
+ if (sId.startsWith("_")) continue;
832
+ sliceIds.add(sId);
833
+ }
834
+ if (!!layerSliceId.remove)
835
+ for (const sId of Object.keys(layerSliceId.remove)) {
836
+ if (sliceIds.has(sId)) {
837
+ sliceIds.delete(sId);
838
+ }
839
+ }
840
+ for (const [sliceId, compRef] of Object.entries(layer.add)) {
841
+ if (sliceId.startsWith("_")) continue;
842
+ add.set(sliceId, compRef);
843
+ }
844
+ if (!!layer.remove)
845
+ for (const sliceId of Object.keys(layer.remove)) {
846
+ if (add.has(sliceId)) {
847
+ add.delete(sliceId);
848
+ }
849
+ }
850
+ return { add: Object.fromEntries(add), sliceIds: Array.from(sliceIds) };
851
+ }
623
852
  async getChildRefs(where, filter) {
624
853
  const { [this._tableKey]: table } = await this.get(where, filter);
625
854
  const childRefs = [];
626
855
  for (const row of table._data) {
627
856
  const layer = row;
628
- for (const [sliceId, compRef] of Object.entries(layer.add)) {
857
+ const resolvedLayer = await this.resolveBaseLayer(layer);
858
+ for (const [sliceId, ref] of Object.entries(resolvedLayer.add)) {
629
859
  if (sliceId.startsWith("_")) continue;
630
860
  childRefs.push({
631
861
  tableKey: layer.componentsTable,
632
- ref: compRef
862
+ ref,
863
+ sliceIds: [sliceId]
633
864
  });
634
865
  }
635
866
  }
636
867
  return childRefs;
637
868
  }
638
- filterRow(row, _, value) {
869
+ async filterRow(row, _, value) {
639
870
  const layer = row;
640
871
  const compRef = value;
641
- for (const componentRef of Object.values(layer.add)) {
872
+ const resolvedLayer = await this.resolveBaseLayer(layer);
873
+ for (const componentRef of Object.values(resolvedLayer.add)) {
642
874
  if (componentRef === compRef) {
643
875
  return true;
644
876
  }
@@ -653,11 +885,21 @@ const createController = async (type, core, tableKey, refs) => {
653
885
  ctrl = new LayerController(core, tableKey, refs);
654
886
  break;
655
887
  case "components":
888
+ case "edits":
889
+ case "editHistory":
890
+ case "multiEdits":
656
891
  ctrl = new ComponentController(core, tableKey, refs);
657
892
  break;
658
893
  case "cakes":
659
894
  ctrl = new CakeController(core, tableKey, refs);
660
895
  break;
896
+ case "sliceIds":
897
+ ctrl = new SliceIdController(
898
+ core,
899
+ tableKey,
900
+ refs
901
+ );
902
+ break;
661
903
  default:
662
904
  throw new Error(`Controller for type ${type} is not implemented yet.`);
663
905
  }
@@ -721,7 +963,7 @@ class Core {
721
963
  const validate = new Validate();
722
964
  validate.addValidator(new BaseValidator());
723
965
  const result = await validate.run(data);
724
- if ((result.hasErrors || result.base && result.base.hasErrors) && !result.base.refsNotFound) {
966
+ if ((result.hasErrors || result.base && result.base.hasErrors) && !result.base.refsNotFound && !result.base.layerBasesNotFound) {
725
967
  throw new Error(
726
968
  "The imported rljson data is not valid:\n" + JSON.stringify(result, null, 2)
727
969
  );
@@ -767,6 +1009,152 @@ class Core {
767
1009
  return this._io.readRows({ table, where });
768
1010
  }
769
1011
  }
1012
+ const inject = (tree, path, value) => {
1013
+ for (let i = 0; i < path.length; i++) {
1014
+ const segment = path[i];
1015
+ if (i === path.length - 1) {
1016
+ tree[segment] = value;
1017
+ delete tree["_hash"];
1018
+ } else {
1019
+ if (!tree[segment]) {
1020
+ tree[segment] = {};
1021
+ }
1022
+ tree = tree[segment];
1023
+ }
1024
+ }
1025
+ };
1026
+ const isolate = (tree, path, preservedKeys = []) => {
1027
+ if (path.length === 0) {
1028
+ return Array.isArray(tree) ? [] : {};
1029
+ }
1030
+ if (tree == null) {
1031
+ return null;
1032
+ }
1033
+ const [currentKey, ...remainingPath] = path;
1034
+ const result = Array.isArray(tree) ? [] : {};
1035
+ if (!Array.isArray(tree)) {
1036
+ for (const key in tree) {
1037
+ if (typeof key === "string" && key.startsWith("_") || preservedKeys.includes(key)) {
1038
+ result[key] = tree[key];
1039
+ }
1040
+ }
1041
+ }
1042
+ if (!(currentKey in tree)) {
1043
+ return result;
1044
+ }
1045
+ const currentValue = tree[currentKey];
1046
+ if (remainingPath.length === 0) {
1047
+ if (Array.isArray(result)) {
1048
+ result[currentKey] = currentValue;
1049
+ } else {
1050
+ result[currentKey] = currentValue;
1051
+ }
1052
+ } else {
1053
+ const isolatedChild = isolate(currentValue, remainingPath, preservedKeys);
1054
+ const hasContent = Array.isArray(isolatedChild) ? isolatedChild.length > 0 : Object.keys(isolatedChild).length > 0;
1055
+ if (hasContent || isolatedChild === null) {
1056
+ if (Array.isArray(result)) {
1057
+ result[currentKey] = isolatedChild;
1058
+ } else {
1059
+ result[currentKey] = isolatedChild;
1060
+ }
1061
+ }
1062
+ }
1063
+ return result;
1064
+ };
1065
+ const mergeTrees = (trees) => {
1066
+ if (!trees || trees.length === 0) {
1067
+ return {};
1068
+ }
1069
+ let result = {};
1070
+ for (const { tree } of trees) {
1071
+ if (tree != null) {
1072
+ result = mergeStructures(result, tree);
1073
+ }
1074
+ }
1075
+ const pathValues = [];
1076
+ for (const { tree, path } of trees) {
1077
+ if (tree == null) continue;
1078
+ let current = tree;
1079
+ let pathExists = true;
1080
+ for (const key of path) {
1081
+ if (current == null || !(key in current)) {
1082
+ pathExists = false;
1083
+ break;
1084
+ }
1085
+ current = current[key];
1086
+ }
1087
+ if (pathExists && current != null) {
1088
+ pathValues.push({ path, value: current });
1089
+ }
1090
+ }
1091
+ const pathGroups = /* @__PURE__ */ new Map();
1092
+ for (const { path, value } of pathValues) {
1093
+ const pathKey = JSON.stringify(path);
1094
+ if (!pathGroups.has(pathKey)) {
1095
+ pathGroups.set(pathKey, []);
1096
+ }
1097
+ pathGroups.get(pathKey).push(value);
1098
+ }
1099
+ for (const [pathKey, values] of pathGroups) {
1100
+ const path = JSON.parse(pathKey);
1101
+ let mergedValue = void 0;
1102
+ for (const value of values) {
1103
+ if (value == null) continue;
1104
+ if (mergedValue === void 0) {
1105
+ mergedValue = value;
1106
+ continue;
1107
+ }
1108
+ if (Array.isArray(mergedValue) && Array.isArray(value)) {
1109
+ mergedValue = [...mergedValue, ...value];
1110
+ } else if (!Array.isArray(mergedValue) && !Array.isArray(value)) {
1111
+ mergedValue = {
1112
+ ...mergedValue,
1113
+ ...value
1114
+ };
1115
+ }
1116
+ }
1117
+ let current = result;
1118
+ for (let i = 0; i < path.length - 1; i++) {
1119
+ const key = path[i];
1120
+ if (current == null || !(key in current)) {
1121
+ current[key] = typeof path[i + 1] === "number" ? [] : {};
1122
+ }
1123
+ current = current[key];
1124
+ }
1125
+ if (path.length > 0) {
1126
+ current[path[path.length - 1]] = mergedValue;
1127
+ }
1128
+ }
1129
+ return result;
1130
+ };
1131
+ function mergeStructures(target, source) {
1132
+ if (source == null) return target;
1133
+ if (target == null) return source;
1134
+ if (Array.isArray(target) && Array.isArray(source)) {
1135
+ const result = [...target];
1136
+ for (let i = 0; i < source.length; i++) {
1137
+ if (result[i] === void 0) {
1138
+ result[i] = source[i];
1139
+ } else {
1140
+ result[i] = mergeStructures(result[i], source[i]);
1141
+ }
1142
+ }
1143
+ return result;
1144
+ }
1145
+ if (typeof target === "object" && typeof source === "object" && !Array.isArray(target) && !Array.isArray(source) && target !== null && source !== null) {
1146
+ const result = { ...target };
1147
+ for (const key in source) {
1148
+ if (key in result) {
1149
+ result[key] = mergeStructures(result[key], source[key]);
1150
+ } else {
1151
+ result[key] = source[key];
1152
+ }
1153
+ }
1154
+ return result;
1155
+ }
1156
+ return source;
1157
+ }
770
1158
  class ColumnSelection {
771
1159
  constructor(columns) {
772
1160
  this._throwOnWrongAlias(columns);
@@ -1113,6 +1501,19 @@ class ColumnSelection {
1113
1501
  }
1114
1502
  ]);
1115
1503
  }
1504
+ static exampleCarsDeeplyNestedColumnSelection() {
1505
+ return new ColumnSelection([
1506
+ {
1507
+ key: "brand",
1508
+ route: "catalogCake/catalogSeriesLayer/catalogSeries/seriesCake/seriesCarsLayer/seriesCars/carCake/carGeneralLayer/carGeneral/brand",
1509
+ alias: "brand",
1510
+ titleLong: "Car Brand",
1511
+ titleShort: "Brand",
1512
+ type: "string",
1513
+ _hash: ""
1514
+ }
1515
+ ]);
1516
+ }
1116
1517
  static exampleCarsColumnSelectionOnlySomeColumns() {
1117
1518
  return new ColumnSelection([
1118
1519
  {
@@ -1638,23 +2039,8 @@ class RowFilterProcessor {
1638
2039
  return remainingIndices;
1639
2040
  }
1640
2041
  for (const i of remainingIndices) {
1641
- const cellValue = join.value(i, columnIndex);
1642
- if (Array.isArray(cellValue)) {
1643
- for (const v of cellValue) {
1644
- if (typeof v === "object" && v !== null && v.hasOwnProperty("_value")) {
1645
- const matchValue = v._value;
1646
- if (filter.matches(matchValue)) {
1647
- result.push(i);
1648
- break;
1649
- }
1650
- } else {
1651
- if (filter.matches(v)) {
1652
- result.push(i);
1653
- break;
1654
- }
1655
- }
1656
- }
1657
- } else {
2042
+ const cellValues = join.value(i, columnIndex);
2043
+ for (const cellValue of cellValues) {
1658
2044
  if (filter.matches(cellValue)) {
1659
2045
  result.push(i);
1660
2046
  }
@@ -1695,9 +2081,11 @@ class RowFilterProcessor {
1695
2081
  if (applyTo[r]) {
1696
2082
  continue;
1697
2083
  }
1698
- const cellValue = join.value(r, columnIndex);
1699
- if (filter.matches(cellValue)) {
1700
- applyTo[r] = true;
2084
+ const cellValues = join.value(r, columnIndex);
2085
+ for (const cellValue of cellValues) {
2086
+ if (filter.matches(cellValue)) {
2087
+ applyTo[r] = true;
2088
+ }
1701
2089
  }
1702
2090
  }
1703
2091
  }
@@ -1717,14 +2105,22 @@ ${availableRoutes.map((a) => `- ${a}`).join("\n")}`
1717
2105
  }
1718
2106
  }
1719
2107
  }
2108
+ const joinPreserveKeys = [
2109
+ "sliceIdsTable",
2110
+ "sliceIdsRow",
2111
+ /*'base',*/
2112
+ "sliceIdsTable",
2113
+ "sliceIdsTableRow",
2114
+ "componentsTable"
2115
+ ];
1720
2116
  class Join {
1721
- constructor(baseRows, _baseColumnSelection, _objectMap) {
1722
- this._baseColumnSelection = _baseColumnSelection;
1723
- this._objectMap = _objectMap;
1724
- this._base = this._hashedRows(baseRows);
1725
- }
1726
2117
  _base = {};
2118
+ _baseColumnSelection;
1727
2119
  _processes = [];
2120
+ constructor(rows, columnSelection) {
2121
+ this._base = this._hashedRows(rows);
2122
+ this._baseColumnSelection = columnSelection;
2123
+ }
1728
2124
  // ...........................................................................
1729
2125
  /**
1730
2126
  * Applies a filter to the join and returns the filtered view
@@ -1753,14 +2149,62 @@ class Join {
1753
2149
  const data = {};
1754
2150
  for (const [sliceId, joinRowH] of Object.entries(this.data)) {
1755
2151
  const cols = [...joinRowH.columns];
2152
+ const insertCols = [];
1756
2153
  for (const col of cols) {
1757
- if (Route.fromFlat(setValue.route).equalsWithoutRefs(col.route))
1758
- col.insert = setValue.value;
1759
- else continue;
2154
+ const insertCol = {
2155
+ ...col
2156
+ //inserts: col.inserts ? [...col.inserts] : [],
2157
+ };
2158
+ if (Route.fromFlat(setValue.route).equalsWithoutRefs(col.route)) {
2159
+ for (const cell of col.value.cell) {
2160
+ if (cell.path.length === 0) {
2161
+ throw new Error(
2162
+ `Join: Error while applying SetValue: Cannot set value for column without paths. Route: ${setValue.route.toString()}.`
2163
+ );
2164
+ }
2165
+ if (cell.path.length > 1) {
2166
+ throw new Error(
2167
+ `Join: Error while applying SetValue: Cannot set value for multiple paths in one cell. Found paths: [${cell.path.join(", ")}] for route: ${setValue.route.toString()}.`
2168
+ );
2169
+ }
2170
+ const cellInsertTree = isolate(
2171
+ { ...col.value.tree },
2172
+ cell.path[0],
2173
+ joinPreserveKeys
2174
+ );
2175
+ inject(cellInsertTree, cell.path[0], setValue.value);
2176
+ const propertyKey = cell.path[0].slice(-1)[0];
2177
+ const insert = {
2178
+ cell: [
2179
+ {
2180
+ ...cell,
2181
+ ...{ value: setValue.value },
2182
+ ...{
2183
+ row: {
2184
+ ...cell.row,
2185
+ ...{ [propertyKey]: setValue.value }
2186
+ }
2187
+ }
2188
+ }
2189
+ ],
2190
+ tree: cellInsertTree,
2191
+ rljson: col.value.rljson
2192
+ };
2193
+ if (insert) {
2194
+ if (insertCol.inserts) insertCol.inserts.push(insert);
2195
+ else insertCol.inserts = [insert];
2196
+ }
2197
+ }
2198
+ }
2199
+ insertCols.push(insertCol);
1760
2200
  }
1761
2201
  data[sliceId] = {
1762
- rowHash: Hash.default.calcHash(cols.map((c) => c.value)),
1763
- columns: cols
2202
+ rowHash: Hash.default.calcHash(
2203
+ insertCols.map(
2204
+ (col) => col.value.cell.flatMap((c) => c.value)
2205
+ )
2206
+ ),
2207
+ columns: insertCols
1764
2208
  };
1765
2209
  }
1766
2210
  const process = {
@@ -1803,15 +2247,15 @@ class Join {
1803
2247
  const data = {};
1804
2248
  for (let i2 = 0; i2 < this.rowCount; i2++) {
1805
2249
  const [sliceId, row] = Object.entries(this.data)[i2];
1806
- const selectedColumns = [];
2250
+ const cols = [];
1807
2251
  for (let j = 0; j < masterColumnIndices.length; j++) {
1808
- selectedColumns.push(row.columns[masterColumnIndices[j]]);
2252
+ cols.push(row.columns[masterColumnIndices[j]]);
1809
2253
  }
1810
2254
  data[sliceId] = {
1811
2255
  rowHash: Hash.default.calcHash(
1812
- selectedColumns.map((c) => c.value)
2256
+ cols.map((col) => col.value.cell.flatMap((c) => c.value))
1813
2257
  ),
1814
- columns: selectedColumns
2258
+ columns: cols
1815
2259
  };
1816
2260
  }
1817
2261
  const process = {
@@ -1850,30 +2294,51 @@ class Join {
1850
2294
  * Returns insert Object of the join
1851
2295
  */
1852
2296
  insert() {
1853
- const cakeInserts = this._insertCakeObjects(this.data);
1854
- const cakeInsertsMergedOfLayerRoutes = /* @__PURE__ */ new Map();
1855
- for (const { [this.cakeRoute.root.tableKey]: cakeInsert } of cakeInserts) {
1856
- const cakeInsertRoute = cakeInsert.route.flat;
1857
- if (!cakeInsertsMergedOfLayerRoutes.has(cakeInsertRoute)) {
1858
- cakeInsertsMergedOfLayerRoutes.set(cakeInsertRoute, cakeInsert.value);
1859
- } else {
1860
- const existingValue = cakeInsertsMergedOfLayerRoutes.get(
1861
- cakeInsertRoute
2297
+ const inserts = [];
2298
+ for (let i = 0; i < this.columnCount; i++) {
2299
+ const colInserts = [];
2300
+ for (const row of Object.values(this.data)) {
2301
+ const col = row.columns[i];
2302
+ if (col.inserts && col.inserts.length > 0) {
2303
+ for (const insert of col.inserts) {
2304
+ for (const cell of insert.cell) {
2305
+ const tree = insert.tree;
2306
+ const path = cell.path;
2307
+ inject(tree, path[0].slice(0, -1), cell.row);
2308
+ colInserts.push({
2309
+ route: col.route,
2310
+ tree,
2311
+ path: path[0]
2312
+ });
2313
+ }
2314
+ }
2315
+ }
2316
+ }
2317
+ if (colInserts.length === 0) continue;
2318
+ const routes = colInserts.map((ins) => ins.route.flat);
2319
+ const uniqueRoute = Array.from(new Set(routes));
2320
+ if (uniqueRoute.length > 1) {
2321
+ throw new Error(
2322
+ `Join: Error while generating insert: Multiple different routes found in inserts: ${uniqueRoute.map((r) => r.toString()).join(", ")}. Cannot generate single insert object.`
1862
2323
  );
1863
- const mergedValue = merge(existingValue, cakeInsert.value);
1864
- cakeInsertsMergedOfLayerRoutes.set(cakeInsertRoute, mergedValue);
1865
2324
  }
2325
+ const merged = mergeTrees(
2326
+ colInserts.map((ins) => ({
2327
+ tree: ins.tree,
2328
+ path: ins.path.slice(0, -1)
2329
+ }))
2330
+ );
2331
+ traverse(merged, ({ parent, key, value }) => {
2332
+ if (key == "_data" && Array.isArray(value) && value.length > 0) {
2333
+ parent[key] = value.filter((v) => !!v);
2334
+ }
2335
+ });
2336
+ inserts.push({
2337
+ route: Route.fromFlat(uniqueRoute[0]).toRouteWithProperty(),
2338
+ tree: merged
2339
+ });
1866
2340
  }
1867
- const results = [];
1868
- for (const [route, value] of cakeInsertsMergedOfLayerRoutes) {
1869
- const insert = {
1870
- command: "add",
1871
- route,
1872
- value
1873
- };
1874
- results.push(insert);
1875
- }
1876
- return results;
2341
+ return inserts;
1877
2342
  }
1878
2343
  // ...........................................................................
1879
2344
  /**
@@ -2020,7 +2485,11 @@ class Join {
2020
2485
  const dataColRoute = dataCol.route;
2021
2486
  return colInfoRoute.equalsWithoutRefs(dataColRoute);
2022
2487
  });
2023
- row.push(joinCol ? joinCol.insert ?? joinCol.value : null);
2488
+ const insertValue = joinCol && joinCol.inserts ? joinCol.inserts.flatMap(
2489
+ (con) => con.cell.flatMap((c) => c.value)
2490
+ ) ?? null : null;
2491
+ const baseValue = joinCol && joinCol.value.cell ? joinCol.value.cell.flatMap((c) => c.value) ?? null : null;
2492
+ row.push(insertValue ?? baseValue);
2024
2493
  }
2025
2494
  result.push(row);
2026
2495
  }
@@ -2029,184 +2498,26 @@ class Join {
2029
2498
  static empty() {
2030
2499
  return new Join({}, ColumnSelection.empty());
2031
2500
  }
2032
- //#############################################################
2033
- // ############# Private Methods ##############################
2034
- //#############################################################
2035
2501
  // ...........................................................................
2036
2502
  /**
2037
- * Builds cake insert objects from the given join rows
2038
- * @param rows - The join rows
2039
- * @returns The cake insert objects
2503
+ * Hashes the given join rows. If insert value is present, it is used for hashing.
2504
+ *
2505
+ * @param rows The join rows to hash
2506
+ * @returns The hashed join rows
2040
2507
  */
2041
- _insertCakeObjects(rows) {
2042
- const cakeInsertObjects = [];
2043
- const cakeRoute = this.cakeRoute;
2044
- for (const [sliceId, row] of Object.entries(rows)) {
2045
- const layerInsertObjectList = this._insertLayerObjects(
2046
- sliceId,
2047
- row.columns
2048
- );
2049
- for (const layerInsertObject of layerInsertObjectList) {
2050
- const cakeInsertObject = {};
2051
- for (const [layerRoute, layerInsertObj] of Object.entries(
2052
- layerInsertObject
2053
- )) {
2054
- cakeInsertObject[cakeRoute.root.tableKey] = {
2055
- route: layerInsertObj.route,
2056
- value: {
2057
- [layerRoute]: layerInsertObj.value
2058
- }
2059
- };
2060
- }
2061
- cakeInsertObjects.push(cakeInsertObject);
2062
- }
2063
- }
2064
- return cakeInsertObjects;
2065
- }
2066
- // ...........................................................................
2067
- /**
2068
- * Wraps component insert objects into layer insert objects
2069
- * @param sliceId - The slice id
2070
- * @param componentInsertObjects - The component insert objects
2071
- * @returns
2072
- */
2073
- _insertLayerObjects(sliceId, insertRow) {
2074
- const layerRoutes = this.layerRoutes;
2075
- const layerInsertObjects = [];
2076
- const insertComponentObjects = this._insertComponentObjects(
2077
- sliceId,
2078
- insertRow
2079
- );
2080
- for (const layerRoute of layerRoutes) {
2081
- for (const [compRouteFlat, compInsertObj] of Object.entries(
2082
- insertComponentObjects
2083
- )) {
2084
- if (!compInsertObj._somethingToInsert) continue;
2085
- const compRoute = Route.fromFlat(compRouteFlat);
2086
- if (layerRoute.includes(compRoute)) {
2087
- const layerInsertObj = {};
2088
- layerInsertObj[layerRoute.root.tableKey] = {
2089
- route: Route.fromFlat(compRouteFlat),
2090
- value: {
2091
- [sliceId]: compInsertObj
2092
- }
2093
- };
2094
- layerInsertObjects.push(layerInsertObj);
2095
- } else {
2096
- continue;
2097
- }
2098
- }
2099
- }
2100
- return layerInsertObjects;
2101
- }
2102
- // ...........................................................................
2103
- /**
2104
- * Merges columns into component insert objects
2105
- * @param insertColumns - The columns to merge
2106
- * @returns
2107
- */
2108
- _insertComponentObjects(sliceId, insertColumns) {
2109
- const columns = this._mergeInsertRow(sliceId, insertColumns);
2110
- return this._denormalizeComponentInserts(columns, this._objectMap || {});
2111
- }
2112
- _denormalizeComponentInserts(columns, objectMap, refTableKey) {
2113
- const result = {};
2114
- for (const [propertyKey, propertyValue] of Object.entries(objectMap)) {
2115
- if (typeof propertyValue === "object") {
2116
- const refObjectMap = {};
2117
- const refTableKey2 = propertyValue._tableKey;
2118
- for (const [refKey, refValue] of Object.entries(
2119
- propertyValue
2120
- )) {
2121
- if (refKey === "_tableKey") continue;
2122
- refObjectMap[refTableKey2 + "/" + refKey] = refValue;
2123
- }
2124
- const refInsert = this._denormalizeComponentInserts(
2125
- columns,
2126
- refObjectMap,
2127
- refTableKey2
2128
- );
2129
- for (const [refRoute, refObject] of Object.entries(refInsert)) {
2130
- const insertObj = { [propertyKey]: refObject };
2131
- result[refRoute] = {
2132
- ...result[refRoute],
2133
- ...insertObj,
2134
- ...{
2135
- _somethingToInsert: result[refRoute]._somethingToInsert || refObject._somethingToInsert
2136
- }
2137
- };
2138
- }
2139
- } else {
2140
- let compKey = Route.fromFlat(propertyValue).upper().flat;
2141
- compKey = refTableKey ? compKey.replace(`/${refTableKey}`, "") : compKey;
2142
- const refPropertyKey = refTableKey ? propertyKey.replace(`${refTableKey}/`, "") : propertyKey;
2143
- if (!result[compKey]) result[compKey] = {};
2144
- if (refTableKey && !result[compKey]._tableKey)
2145
- result[compKey]._tableKey = refTableKey;
2146
- const propValue = columns.find((col) => {
2147
- return col.route.propertyKey === propertyKey;
2148
- });
2149
- if (!propValue) {
2150
- throw new Error(
2151
- `Join._denormalizeComponentInserts: Could not find column value for property key "${propertyKey}".`
2152
- );
2153
- }
2154
- const somethingToInsert = !!propValue.insert;
2155
- result[compKey] = {
2156
- ...result[compKey],
2157
- [refPropertyKey]: propValue.insert ?? propValue.value,
2158
- ...{
2159
- _somethingToInsert: result[compKey]._somethingToInsert || somethingToInsert
2160
- }
2161
- };
2162
- }
2163
- }
2164
- return result;
2165
- }
2166
- // ...........................................................................
2167
- /**
2168
- * Merges the insert values into the base row
2169
- * @param sliceId - The slice id
2170
- * @param insertRow - The insert row
2171
- * @returns The merged join row
2172
- */
2173
- _mergeInsertRow(sliceId, insertRow) {
2174
- const baseColumns = this._base[sliceId].columns;
2175
- const mergedRow = [];
2176
- for (const baseCol of baseColumns) {
2177
- const insertCol = insertRow.find(
2178
- (col) => col.route.equalsWithoutRefs(baseCol.route)
2179
- );
2180
- if (insertCol) {
2181
- mergedRow.push({
2182
- route: baseCol.route,
2183
- value: baseCol.value,
2184
- insert: insertCol.insert
2185
- });
2186
- } else {
2187
- mergedRow.push(baseCol);
2188
- }
2189
- }
2190
- return mergedRow;
2191
- }
2192
- // ...........................................................................
2193
- /**
2194
- * Hashes the given join rows. If insert value is present, it is used for hashing.
2195
- *
2196
- * @param rows The join rows to hash
2197
- * @returns The hashed join rows
2198
- */
2199
- _hashedRows(rows) {
2200
- const sliceIds = Object.keys(rows);
2201
- const hashedRows = {};
2202
- for (const sliceId of sliceIds) {
2203
- const columns = rows[sliceId];
2204
- const rowHash = Hash.default.calcHash(
2205
- columns.map((col) => col.insert ?? col.value)
2508
+ _hashedRows(rows) {
2509
+ const sliceIds = Object.keys(rows);
2510
+ const hashedRows = {};
2511
+ for (const sliceId of sliceIds) {
2512
+ const cols = rows[sliceId];
2513
+ const rowHash = Hash.default.calcHash(
2514
+ cols.map(
2515
+ (col) => col.inserts?.flatMap((con) => con.cell.flatMap((c) => c.value)) ?? col.value.cell ? col.value.cell.flatMap((c) => c.value) : []
2516
+ )
2206
2517
  );
2207
2518
  hashedRows[sliceId] = {
2208
2519
  rowHash,
2209
- columns
2520
+ columns: cols
2210
2521
  };
2211
2522
  }
2212
2523
  return hashedRows;
@@ -2276,6 +2587,25 @@ class Notify {
2276
2587
  return this._callbacks.get(route.flat) || [];
2277
2588
  }
2278
2589
  }
2590
+ const makeUniqueArrayByHash = (arr) => {
2591
+ const seen = /* @__PURE__ */ new Map();
2592
+ const result = [];
2593
+ for (const item of arr) {
2594
+ if (!seen.has(item._hash)) {
2595
+ seen.set(item._hash, item);
2596
+ result.push(item);
2597
+ }
2598
+ }
2599
+ return result;
2600
+ };
2601
+ const makeUnique = (rljson) => {
2602
+ traverse(rljson, ({ parent, key, value }) => {
2603
+ if (key == "_data" && Array.isArray(value)) {
2604
+ parent[key] = makeUniqueArrayByHash(value);
2605
+ }
2606
+ });
2607
+ return rljson;
2608
+ };
2279
2609
  class Db {
2280
2610
  /**
2281
2611
  * Constructor
@@ -2300,22 +2630,27 @@ class Db {
2300
2630
  * Get data from a route with optional filtering
2301
2631
  * @param route - The route to get data from
2302
2632
  * @param where - Optional filter to apply to the data
2633
+ * @param filter - Optional filter to apply to child entries in related tables
2634
+ * @param sliceIds - Optional slice IDs to filter the data
2303
2635
  * @returns An array of Rljson objects matching the route and filter
2304
2636
  * @throws {Error} If the route is not valid or if any controller cannot be created
2305
2637
  */
2306
- async get(route, where) {
2638
+ async get(route, where, filter, sliceIds) {
2307
2639
  if (!route.isValid) throw new Error(`Route ${route.flat} is not valid.`);
2308
2640
  const isolatedRoute = await this.isolatePropertyKeyFromRoute(route);
2309
- const cacheHash = `${isolatedRoute.flat}|${JSON.stringify(where)}`;
2310
- const isCached = this._cache.has(cacheHash);
2311
- if (isCached) {
2312
- return this._cache.get(cacheHash);
2313
- } else {
2314
- const controllers = await this._indexedControllers(isolatedRoute);
2315
- const data = await this._get(isolatedRoute, where, controllers);
2316
- this._cache.set(cacheHash, data);
2317
- return data;
2318
- }
2641
+ const controllers = await this.indexedControllers(isolatedRoute);
2642
+ const data = await this._get(
2643
+ isolatedRoute,
2644
+ where,
2645
+ controllers,
2646
+ filter,
2647
+ sliceIds
2648
+ );
2649
+ const dataWithControllers = {
2650
+ ...data,
2651
+ ...{ controllers }
2652
+ };
2653
+ return dataWithControllers;
2319
2654
  }
2320
2655
  // ...........................................................................
2321
2656
  /**
@@ -2326,37 +2661,108 @@ class Db {
2326
2661
  * @param where - The recursive filtering key/value pairs to apply to the data
2327
2662
  * @param controllers - The controllers to use for fetching data
2328
2663
  * @param filter - Optional filter to apply to the data at the current route segment
2664
+ * @param sliceIds - Optional slice IDs to filter the data at the current route segment
2329
2665
  * @returns - An Rljson object matching the route and filters
2330
2666
  */
2331
- async _get(route, where, controllers, filter) {
2667
+ async _get(route, where, controllers, filter, sliceIds, routeAccumulator) {
2668
+ const params = {
2669
+ route: route.flat,
2670
+ where,
2671
+ filter,
2672
+ sliceIds,
2673
+ routeAccumulator: routeAccumulator ? routeAccumulator.flat : ""
2674
+ };
2675
+ const cacheHash = hsh(rmhsh(params))._hash;
2676
+ const isCached = this._cache.has(cacheHash);
2677
+ if (isCached) {
2678
+ return this._cache.get(cacheHash);
2679
+ }
2332
2680
  const nodeTableKey = route.top.tableKey;
2333
2681
  const nodeRoute = route;
2334
2682
  const nodeRouteRef = await this._getReferenceOfRouteSegment(nodeRoute.top);
2335
2683
  const nodeController = controllers[nodeTableKey];
2684
+ const nodeSliceIds = nodeRoute.top.sliceIds ?? sliceIds;
2336
2685
  let nodeWhere = typeof where === "object" ? { ...where } : where;
2337
2686
  if (!route.isRoot && typeof nodeWhere === "object") {
2338
2687
  delete nodeWhere[nodeRoute.deeper().top.tableKey];
2339
2688
  }
2340
2689
  nodeWhere = nodeWhere ? typeof nodeWhere === "string" ? { _hash: nodeWhere } : nodeWhere : {};
2341
2690
  if (nodeRouteRef && nodeRouteRef.length > 0)
2342
- nodeWhere = { ...nodeWhere, ...{ _hash: nodeRouteRef } };
2691
+ nodeWhere = { _hash: nodeRouteRef };
2343
2692
  delete nodeWhere["_through"];
2344
2693
  delete nodeWhere["_tableKey"];
2345
2694
  const {
2346
2695
  [nodeTableKey]: { _data: nodeRows, _type: nodeType, _hash: nodeHash }
2347
2696
  } = await nodeController.get(nodeWhere);
2697
+ const nodeColumnCfgs = nodeController.tableCfg().columns;
2348
2698
  const nodeRowsFiltered = [];
2349
- if (filter && filter.length > 0) {
2350
- for (const f of filter) {
2351
- if (f.tableKey !== nodeTableKey) continue;
2352
- for (const nodeRow of nodeRows) {
2699
+ for (const nodeRow of nodeRows) {
2700
+ const filterActive = filter && filter.length > 0;
2701
+ const sliceIdActive = nodeSliceIds && nodeSliceIds.length > 0;
2702
+ if (!filterActive && !sliceIdActive) {
2703
+ nodeRowsFiltered.push(nodeRow);
2704
+ continue;
2705
+ }
2706
+ let filterResult = false;
2707
+ const filterProperties = [];
2708
+ if (filterActive) {
2709
+ for (const f of filter) {
2710
+ if (f.tableKey !== nodeTableKey) continue;
2353
2711
  if (nodeRow._hash === f.ref) {
2354
- nodeRowsFiltered.push(nodeRow);
2712
+ filterProperties.push(f);
2713
+ filterResult = true;
2355
2714
  }
2356
2715
  }
2716
+ } else {
2717
+ filterResult = true;
2718
+ }
2719
+ let sliceIdResult = false;
2720
+ if (sliceIdActive) {
2721
+ switch (nodeType) {
2722
+ case "cakes":
2723
+ const cake = nodeRow;
2724
+ const cakeSliceIds = await this._resolveSliceIds(
2725
+ cake.sliceIdsTable,
2726
+ cake.sliceIdsRow
2727
+ );
2728
+ const cakeMatchesSliceIds = nodeSliceIds.filter(
2729
+ (sId) => cakeSliceIds.includes(sId)
2730
+ );
2731
+ if (cakeMatchesSliceIds.length > 0) sliceIdResult = true;
2732
+ break;
2733
+ case "layers":
2734
+ const layer = nodeRow;
2735
+ const layerSliceIds = await this._resolveSliceIds(
2736
+ layer.sliceIdsTable,
2737
+ layer.sliceIdsTableRow
2738
+ );
2739
+ const layerMatchesSliceIds = nodeSliceIds.filter(
2740
+ (sId) => layerSliceIds.includes(sId)
2741
+ );
2742
+ if (layerMatchesSliceIds.length > 0) sliceIdResult = true;
2743
+ break;
2744
+ case "components":
2745
+ if (filterProperties.length > 0) {
2746
+ const componentSliceIds = filterProperties.flatMap(
2747
+ (f) => f.sliceIds
2748
+ );
2749
+ const componentMatchesSliceIds = nodeSliceIds.filter(
2750
+ (sId) => componentSliceIds.includes(sId)
2751
+ );
2752
+ if (componentMatchesSliceIds.length > 0) {
2753
+ sliceIdResult = true;
2754
+ }
2755
+ }
2756
+ break;
2757
+ /* v8 ignore next -- @preserve */
2758
+ default:
2759
+ sliceIdResult = true;
2760
+ break;
2761
+ }
2762
+ } else {
2763
+ sliceIdResult = true;
2357
2764
  }
2358
- } else {
2359
- nodeRowsFiltered.push(...nodeRows);
2765
+ if (filterResult && sliceIdResult) nodeRowsFiltered.push(nodeRow);
2360
2766
  }
2361
2767
  const node = {
2362
2768
  [nodeTableKey]: {
@@ -2365,11 +2771,48 @@ class Db {
2365
2771
  _hash: nodeHash
2366
2772
  }
2367
2773
  };
2774
+ const nodeValue = node[nodeTableKey]._data.filter(
2775
+ (v) => v !== void 0 && v !== null
2776
+ );
2368
2777
  if (route.isRoot) {
2369
2778
  if (route.hasPropertyKey) {
2370
- return this.isolatePropertyFromComponents(node, route.propertyKey);
2371
- }
2372
- return node;
2779
+ const isolatedNode = this.isolatePropertyFromComponents(
2780
+ node,
2781
+ route.propertyKey
2782
+ );
2783
+ const result3 = {
2784
+ rljson: isolatedNode,
2785
+ tree: { [nodeTableKey]: node[nodeTableKey] },
2786
+ cell: nodeValue.map(
2787
+ (v, idx) => ({
2788
+ value: v[route.propertyKey] ?? null,
2789
+ row: v,
2790
+ route: Route.fromFlat(
2791
+ (routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "") + `/${route.propertyKey}`
2792
+ ).toRouteWithProperty(),
2793
+ path: [[nodeTableKey, "_data", idx, route.propertyKey]]
2794
+ })
2795
+ )
2796
+ };
2797
+ this._cache.set(cacheHash, result3);
2798
+ return result3;
2799
+ }
2800
+ const result2 = {
2801
+ rljson: node,
2802
+ tree: { [nodeTableKey]: node[nodeTableKey] },
2803
+ cell: nodeValue.map(
2804
+ (v, idx) => ({
2805
+ value: v[route.propertyKey] ?? null,
2806
+ row: v,
2807
+ route: Route.fromFlat(
2808
+ (routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "")
2809
+ ),
2810
+ path: [[nodeTableKey, "_data", idx]]
2811
+ })
2812
+ )
2813
+ };
2814
+ this._cache.set(cacheHash, result2);
2815
+ return result2;
2373
2816
  }
2374
2817
  const childrenRoute = route.deeper();
2375
2818
  const childrenTableKey = childrenRoute.top.tableKey;
@@ -2379,21 +2822,60 @@ class Db {
2379
2822
  const nodeRowsMatchingChildrenRefs = /* @__PURE__ */ new Map();
2380
2823
  for (let i = 0; i < nodeRowsFiltered.length; i++) {
2381
2824
  const nodeRow = nodeRowsFiltered[i];
2825
+ const nodeRowObj = { ...{}, ...nodeRow };
2382
2826
  const childrenRefs = await nodeController.getChildRefs(
2383
2827
  nodeRow._hash
2384
2828
  );
2385
- const rowChildren = await this._get(
2829
+ const childrenRefTypes = /* @__PURE__ */ new Map();
2830
+ const childrenRefSliceIds = /* @__PURE__ */ new Set();
2831
+ for (const cr of childrenRefs) {
2832
+ if (!!cr.columnKey) {
2833
+ const childrenRefColumnCfg = nodeColumnCfgs.find(
2834
+ (c) => c.key === cr.columnKey
2835
+ );
2836
+ if (childrenRefColumnCfg) {
2837
+ childrenRefTypes.set(
2838
+ childrenRefColumnCfg.key,
2839
+ childrenRefColumnCfg.ref?.type ?? ""
2840
+ );
2841
+ }
2842
+ if (cr.sliceIds && cr.sliceIds.length > 0) {
2843
+ for (const sId of cr.sliceIds) {
2844
+ childrenRefSliceIds.add(sId);
2845
+ }
2846
+ }
2847
+ }
2848
+ }
2849
+ if (childrenRefTypes.size > 1) {
2850
+ throw new Error(
2851
+ `Db._get: Multiple reference types found for children of table ${nodeTableKey}.`
2852
+ );
2853
+ }
2854
+ const cakeIsReferenced = childrenRefTypes.size === 1 && [...childrenRefTypes.values()][0] === "cakes";
2855
+ const componentIsReferenced = childrenRefTypes.size === 1 && nodeType === "components" && [...childrenRefTypes.values()][0] === "components";
2856
+ const childrenSliceIds = cakeIsReferenced ? [...childrenRefSliceIds] : componentIsReferenced ? void 0 : nodeSliceIds;
2857
+ const {
2858
+ rljson: rowChildrenRljson,
2859
+ tree: rowChildrenTree,
2860
+ cell: rowChildrenCell
2861
+ } = await this._get(
2386
2862
  childrenRoute,
2387
2863
  childrenWhere,
2388
2864
  controllers,
2389
- childrenRefs
2865
+ childrenRefs,
2866
+ childrenSliceIds,
2867
+ Route.fromFlat(
2868
+ (routeAccumulator ? routeAccumulator.flat : nodeTableKey) + (nodeHash ? `@${nodeHash}` : "") + "/" + childrenTableKey
2869
+ )
2390
2870
  );
2391
- if (rowChildren[childrenTableKey]._data.length === 0) continue;
2392
- nodeChildrenArray.push(rowChildren);
2871
+ if (cakeIsReferenced) {
2872
+ const refKey = [...childrenRefTypes.keys()][0];
2873
+ nodeRowObj[refKey] = rowChildrenTree;
2874
+ }
2875
+ if (rowChildrenRljson[childrenTableKey]._data.length === 0) continue;
2876
+ nodeChildrenArray.push(rowChildrenRljson);
2393
2877
  if (childrenThroughProperty) {
2394
- const childrenHashes = rowChildren[childrenTableKey]._data.map(
2395
- (rc) => rc._hash
2396
- );
2878
+ const resolvedChildrenHashes = rowChildrenRljson[childrenTableKey]._data.map((rc) => rc._hash);
2397
2879
  for (const nr of nodeRowsFiltered) {
2398
2880
  {
2399
2881
  const throughHashesInRowCouldBeArray = nr[childrenThroughProperty];
@@ -2401,30 +2883,191 @@ class Db {
2401
2883
  throughHashesInRowCouldBeArray
2402
2884
  ) ? throughHashesInRowCouldBeArray : [throughHashesInRowCouldBeArray];
2403
2885
  for (const th of throughHashesInRow) {
2404
- if (childrenHashes.includes(th)) {
2405
- nodeRowsMatchingChildrenRefs.set(nr._hash, nr);
2886
+ if (resolvedChildrenHashes.includes(th)) {
2887
+ nodeRowObj[childrenThroughProperty] = {
2888
+ ...rowChildrenTree[childrenTableKey],
2889
+ ...{ _tableKey: childrenTableKey }
2890
+ };
2406
2891
  }
2407
2892
  }
2408
2893
  }
2409
2894
  }
2410
2895
  }
2896
+ const resolvedChildren = rowChildrenRljson[childrenTableKey]._data;
2897
+ const childrenRefsOfRow = childrenRefs.filter(
2898
+ (cr) => cr.tableKey == childrenTableKey
2899
+ );
2900
+ const matchingChildrenRefs = childrenRefsOfRow.filter(
2901
+ (cr) => !!resolvedChildren.find((ch) => cr.ref === ch._hash)
2902
+ );
2903
+ if (nodeType === "layers") {
2904
+ const compChildrenTrees = rowChildrenTree[childrenTableKey]._data;
2905
+ const compChildrenPaths = rowChildrenCell.map((c) => c.path);
2906
+ const components = compChildrenTrees.map((c, idx) => {
2907
+ return {
2908
+ tree: c,
2909
+ path: compChildrenPaths.filter((p) => p[0][2] == idx)
2910
+ };
2911
+ });
2912
+ const layerTreesAndPaths = components.map(({ tree: comp, path }) => {
2913
+ const sliceIds2 = matchingChildrenRefs.find(
2914
+ (cr) => cr.ref === comp._hash
2915
+ )?.sliceIds;
2916
+ if (!sliceIds2 || sliceIds2.length === 0) {
2917
+ throw new Error(
2918
+ `Db._get: No sliceIds found for component ${comp._hash} of layer ${nodeRow._hash}.`
2919
+ );
2920
+ }
2921
+ if (sliceIds2.length > 1) {
2922
+ throw new Error(
2923
+ `Db._get: Multiple sliceIds found for component ${comp._hash} of layer ${nodeRow._hash}.`
2924
+ );
2925
+ }
2926
+ const sliceId = sliceIds2[0];
2927
+ const pathsForSliceId = [
2928
+ ...path.map((p) => p[0]).map((p) => {
2929
+ const newPath = [...p];
2930
+ newPath[2] = 0;
2931
+ return ["add", sliceId, ...newPath];
2932
+ })
2933
+ ];
2934
+ return {
2935
+ [sliceId]: {
2936
+ tree: {
2937
+ [childrenTableKey]: {
2938
+ ...{ _data: [comp] },
2939
+ ...{ _type: "components" }
2940
+ }
2941
+ },
2942
+ path: pathsForSliceId
2943
+ }
2944
+ };
2945
+ });
2946
+ const layer = layerTreesAndPaths.flatMap(
2947
+ (ltap) => Object.entries(ltap).map(([sliceId, { tree }]) => ({
2948
+ [sliceId]: tree
2949
+ }))
2950
+ ).reduce((a, b) => ({ ...a, ...b }), {});
2951
+ const paths = layerTreesAndPaths.map(
2952
+ (ltap) => Object.values(ltap)[0].path
2953
+ );
2954
+ nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
2955
+ rljson: nodeRow,
2956
+ tree: {
2957
+ ...nodeRowObj,
2958
+ add: { ...nodeRowObj.add, ...layer }
2959
+ },
2960
+ cell: rowChildrenCell.map(
2961
+ (c, idx) => ({
2962
+ ...c,
2963
+ ...{
2964
+ path: [paths.flat()[idx]]
2965
+ }
2966
+ })
2967
+ )
2968
+ });
2969
+ } else if (nodeType === "cakes") {
2970
+ nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
2971
+ rljson: nodeRow,
2972
+ tree: {
2973
+ ...nodeRowObj,
2974
+ layers: { ...nodeRowObj.layers, ...rowChildrenTree }
2975
+ },
2976
+ cell: rowChildrenCell.map((c) => ({
2977
+ ...c,
2978
+ ...{
2979
+ path: c.path.map((p) => ["layers", ...p])
2980
+ }
2981
+ }))
2982
+ });
2983
+ } else if (nodeType === "components") {
2984
+ if (rowChildrenTree && Object.keys(rowChildrenTree).length > 0) {
2985
+ const columnReferenceMap = nodeColumnCfgs.filter(
2986
+ (c) => c.ref && ["components", "cakes"].includes(c.ref.type)
2987
+ ).reduce((acc, curr) => {
2988
+ acc.set(curr.key, curr.ref.tableKey);
2989
+ return acc;
2990
+ }, /* @__PURE__ */ new Map());
2991
+ const resolvedRefs = {};
2992
+ for (const [colKey, childTableKey] of columnReferenceMap) {
2993
+ const tree = {
2994
+ ...rowChildrenTree[childTableKey],
2995
+ ...{ _tableKey: childTableKey }
2996
+ };
2997
+ const cell = rowChildrenCell.map((c) => ({
2998
+ ...c,
2999
+ ...{
3000
+ path: c.path.filter((p) => p[0] === childTableKey).map((p) => [colKey, ...p.slice(1)])
3001
+ }
3002
+ }));
3003
+ resolvedRefs[colKey] = { tree, cell };
3004
+ }
3005
+ const resolvedProperties = Object.entries(resolvedRefs).map(([colKey, { tree }]) => ({
3006
+ [colKey]: tree
3007
+ })).reduce((a, b) => ({ ...a, ...b }), {});
3008
+ const resolvedTree = {
3009
+ ...nodeRowObj,
3010
+ ...resolvedProperties
3011
+ };
3012
+ const resolvedCell = Object.values(resolvedRefs).map((r) => r.cell).flat();
3013
+ nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
3014
+ rljson: nodeRow,
3015
+ tree: resolvedTree,
3016
+ cell: resolvedCell
3017
+ });
3018
+ } else {
3019
+ nodeRowsMatchingChildrenRefs.set(nodeRow._hash, {
3020
+ rljson: nodeRow,
3021
+ tree: { ...nodeRowObj },
3022
+ cell: rowChildrenCell
3023
+ });
3024
+ }
3025
+ } else {
3026
+ throw new Error(
3027
+ `Db._get: Unsupported node type ${nodeType} for getting children.`
3028
+ );
3029
+ }
2411
3030
  }
2412
- const nodeChildren = merge(...nodeChildrenArray);
2413
- if (childrenThroughProperty) {
2414
- const matchedNodeRows = Array.from(nodeRowsMatchingChildrenRefs.values());
2415
- return {
3031
+ const nodeChildren = makeUnique(
3032
+ merge(...nodeChildrenArray)
3033
+ );
3034
+ const matchedNodeRows = Array.from(nodeRowsMatchingChildrenRefs.values());
3035
+ const result = {
3036
+ rljson: {
2416
3037
  ...node,
2417
3038
  ...{
2418
3039
  [nodeTableKey]: {
2419
- _data: matchedNodeRows,
2420
- _type: nodeType,
2421
- _hash: nodeHash
3040
+ ...{
3041
+ _data: matchedNodeRows.map((mr) => mr.rljson),
3042
+ _type: nodeType
3043
+ },
3044
+ ...{
3045
+ ...nodeHash ? { _hash: nodeHash } : {}
3046
+ }
2422
3047
  }
2423
3048
  },
2424
3049
  ...nodeChildren
2425
- };
2426
- }
2427
- return { ...node, ...nodeChildren };
3050
+ },
3051
+ tree: {
3052
+ [nodeTableKey]: {
3053
+ ...{
3054
+ _data: matchedNodeRows.map((mr) => mr.tree),
3055
+ _type: nodeType
3056
+ },
3057
+ ...{
3058
+ ...nodeHash ? { _hash: nodeHash } : {}
3059
+ }
3060
+ }
3061
+ },
3062
+ cell: matchedNodeRows.map(
3063
+ (mr, idx) => mr.cell.map((c) => ({
3064
+ ...c,
3065
+ ...{ path: c.path.map((p) => [nodeTableKey, "_data", idx, ...p]) }
3066
+ }))
3067
+ ).flat()
3068
+ };
3069
+ this._cache.set(cacheHash, result);
3070
+ return result;
2428
3071
  }
2429
3072
  // ...........................................................................
2430
3073
  /**
@@ -2451,234 +3094,66 @@ class Db {
2451
3094
  * @param rljson - The Rljson to join data for
2452
3095
  */
2453
3096
  async join(columnSelection, cakeKey, cakeRef) {
2454
- const data = await this._getBaseDataForColumnSelection(columnSelection);
2455
- const cakesTable = data[cakeKey];
2456
- const cake = cakesTable._data.find((c) => c._hash === cakeRef);
2457
- if (!cake) {
3097
+ const {
3098
+ tree: { [cakeKey]: cakesTable }
3099
+ } = await this.get(Route.fromFlat(`${cakeKey}@${cakeRef}`), {});
3100
+ const cakes = cakesTable._data;
3101
+ if (cakes.length === 0) {
2458
3102
  throw new Error(
2459
3103
  `Db.join: Cake with ref "${cakeRef}" not found in cake table "${cakeKey}".`
2460
3104
  );
2461
3105
  }
2462
- const layers = /* @__PURE__ */ new Map();
2463
- for (const layerKey of Object.keys(cake.layers)) {
2464
- if (!data[layerKey]) continue;
2465
- const layersTable = data[layerKey];
2466
- const layer = layersTable._data.find(
2467
- (l) => l._hash === cake.layers[layerKey]
3106
+ if (cakes.length > 1) {
3107
+ throw new Error(
3108
+ `Db.join: Multiple cakes with ref "${cakeRef}" found in cake table "${cakeKey}".`
2468
3109
  );
2469
- layers.set(layerKey, layer);
2470
- }
2471
- const components = /* @__PURE__ */ new Map();
2472
- for (const [tableKey, table] of Object.entries(data)) {
2473
- if (table._type !== "components") continue;
2474
- components.set(tableKey, table);
2475
- }
2476
- const mergedSliceIds = /* @__PURE__ */ new Set();
2477
- for (const layer of layers.values()) {
2478
- const sliceIdsTable = layer.sliceIdsTable;
2479
- const sliceIdsTableRow = layer.sliceIdsTableRow;
2480
- const {
2481
- [sliceIdsTable]: { _data: sliceIds }
2482
- } = await this.core.readRows(sliceIdsTable, { _hash: sliceIdsTableRow });
2483
- for (const sid of sliceIds) {
2484
- for (const s of sid.add) {
2485
- mergedSliceIds.add(s);
2486
- }
2487
- }
2488
3110
  }
2489
- const columnCfgs = /* @__PURE__ */ new Map();
2490
- const columnInfos = /* @__PURE__ */ new Map();
2491
- let objectMap = {};
2492
- for (const [layerKey, layer] of layers.entries()) {
2493
- const componentKey = layer.componentsTable;
2494
- const componentResolved = await this._resolveComponent(
2495
- `${cakeKey}/${layerKey}/`,
2496
- componentKey
2497
- );
2498
- objectMap = { ...objectMap, ...componentResolved.objectMap };
2499
- columnInfos.set(componentKey, componentResolved.columnInfos);
2500
- columnCfgs.set(componentKey, componentResolved.columnCfgs);
2501
- }
2502
- const rowMap = /* @__PURE__ */ new Map();
2503
- const joinColumnInfos = /* @__PURE__ */ new Map();
2504
- for (const sliceId of mergedSliceIds) {
2505
- const sliceIdRow = [];
2506
- for (const [layerKey, layer] of layers.entries()) {
2507
- const layerRef = layer._hash;
2508
- const componentKey = layer.componentsTable;
2509
- const componentRef = layer.add[sliceId];
2510
- const componentsTable = data[componentKey];
2511
- const rowComponentProperties = componentsTable._data.find(
2512
- (r) => r._hash === componentRef
2513
- );
2514
- const resolvedProperties = this._resolveComponentProperties(
2515
- rowComponentProperties,
2516
- objectMap,
2517
- data
2518
- );
2519
- const joinColumns = [];
2520
- for (const [
2521
- resolvedPropertyKey,
2522
- resolvedPropertyValue
2523
- ] of Object.entries(resolvedProperties)) {
2524
- const propertyRoute = cakeKey + "/" + layerKey + "/" + componentKey + "/" + resolvedPropertyKey;
2525
- const propertyColumnInfo = columnInfos.get(componentKey).find((cI) => cI.route === propertyRoute);
2526
- if (!!propertyColumnInfo) {
2527
- const joinColumnRoute = Route.fromFlat(
2528
- `${cakeKey}@${cakeRef}/${layerKey}@${layerRef}/${componentKey}@${componentRef}`
2529
- );
2530
- joinColumnRoute.propertyKey = resolvedPropertyKey;
2531
- joinColumnInfos.set(resolvedPropertyKey, {
2532
- ...propertyColumnInfo,
2533
- key: resolvedPropertyKey,
2534
- route: joinColumnRoute.flatWithoutRefs.slice(1)
2535
- });
2536
- joinColumns.push({
2537
- route: joinColumnRoute,
2538
- value: resolvedPropertyValue ?? null,
2539
- insert: null
2540
- });
2541
- }
2542
- }
2543
- sliceIdRow.push(...joinColumns);
2544
- }
2545
- rowMap.set(sliceId, sliceIdRow);
2546
- }
2547
- const joinRows = {};
2548
- for (const [sliceId, joinColumns] of rowMap.entries()) {
2549
- Object.assign(joinRows, {
2550
- [sliceId]: joinColumns
2551
- });
2552
- }
2553
- return new Join(
2554
- joinRows,
2555
- new ColumnSelection(Array.from(joinColumnInfos.values())),
2556
- objectMap
2557
- ).select(columnSelection);
2558
- }
2559
- _resolveComponentProperties(componentData, objectMapOrRoute, baseData) {
2560
- let result = {};
2561
- for (const [propertyKey, propertyObjectMap] of Object.entries(
2562
- objectMapOrRoute
2563
- )) {
2564
- if (propertyKey === "_tableKey") continue;
2565
- if (typeof propertyObjectMap === "object") {
2566
- const refs = Array.isArray(componentData[propertyKey]) ? componentData[propertyKey] : [componentData[propertyKey]];
2567
- const refTableKey = propertyObjectMap["_tableKey"];
2568
- const refCompTable = baseData[refTableKey];
2569
- if (!refCompTable) continue;
2570
- const prefixedResolvedRefCompData = {};
2571
- for (const ref of refs) {
2572
- const refCompData = refCompTable._data.find((r) => r._hash === ref);
2573
- if (refCompData) {
2574
- const resolvedRefCompData = this._resolveComponentProperties(
2575
- refCompData,
2576
- propertyObjectMap,
2577
- baseData
2578
- );
2579
- for (const [refPropKey, value] of Object.entries(
2580
- resolvedRefCompData
2581
- )) {
2582
- if (!prefixedResolvedRefCompData[`${refTableKey}/${refPropKey}`]) {
2583
- prefixedResolvedRefCompData[`${refTableKey}/${refPropKey}`] = [];
2584
- }
2585
- prefixedResolvedRefCompData[`${refTableKey}/${refPropKey}`].push({
2586
- _ref: ref,
2587
- _value: value
2588
- });
2589
- }
2590
- }
2591
- result = {
2592
- ...result,
2593
- ...prefixedResolvedRefCompData
2594
- };
2595
- }
2596
- }
2597
- result = {
2598
- ...result,
2599
- ...{ [propertyKey]: componentData[propertyKey] }
2600
- };
2601
- }
2602
- return result;
2603
- }
2604
- // ...........................................................................
2605
- /**
2606
- * Resolve a component's columns, including referenced components
2607
- *
2608
- * @param baseRoute - The base route for the component
2609
- * @param componentKey - The component's table key
2610
- * @returns - The resolved column configurations, column infos, and object map
2611
- */
2612
- async _resolveComponent(baseRoute, componentKey) {
2613
- const { columns: colCfgs } = await this.core.tableCfg(componentKey);
2614
- const objectMap = {};
2615
- const columnCfgs = [];
2616
- const columnInfos = [];
2617
- for (let i = 0; i < colCfgs.length; i++) {
2618
- if (colCfgs[i].key === "_hash") continue;
2619
- const colCfg = colCfgs[i];
2620
- if (colCfg.ref) {
2621
- const columnCfgsAndInfosForRef = await this._resolveComponent(
2622
- baseRoute + `/${componentKey}`,
2623
- colCfg.ref.tableKey
3111
+ const cake = cakes[0];
3112
+ const sliceIds = await this._resolveSliceIds(
3113
+ cake.sliceIdsTable,
3114
+ cake.sliceIdsRow
3115
+ );
3116
+ const rows = {};
3117
+ for (const sliceId of sliceIds) {
3118
+ const row = [];
3119
+ for (const columnInfo of columnSelection.columns) {
3120
+ const columnRoute = Route.fromFlat(
3121
+ columnInfo.route
3122
+ ).toRouteWithProperty();
3123
+ const columnContainer = await this.get(
3124
+ columnRoute,
3125
+ cakeRef,
3126
+ void 0,
3127
+ [sliceId]
2624
3128
  );
2625
- objectMap[colCfg.key] = {
2626
- _tableKey: colCfg.ref.tableKey,
2627
- ...columnCfgsAndInfosForRef.objectMap
3129
+ const column = {
3130
+ route: columnRoute,
3131
+ value: columnContainer,
3132
+ inserts: null
2628
3133
  };
2629
- const columnCfgsForRef = columnCfgsAndInfosForRef.columnCfgs.map(
2630
- (cc) => ({
2631
- ...cc,
2632
- key: colCfg.ref.tableKey + "/" + cc.key
2633
- })
2634
- );
2635
- const columnInfosForRef = columnCfgsAndInfosForRef.columnInfos.map(
2636
- (cc) => ({
2637
- ...cc,
2638
- key: colCfg.ref.tableKey + "/" + cc.key
2639
- })
2640
- );
2641
- columnCfgs.push(...columnCfgsForRef);
2642
- columnInfos.push(...columnInfosForRef);
2643
- }
2644
- const columnRoute = Route.fromFlat(
2645
- baseRoute.length > 0 ? `${baseRoute}/${componentKey}/${colCfg.key}` : `/${componentKey}/${colCfg.key}`
2646
- ).flat.slice(1);
2647
- if (!objectMap[colCfg.key]) objectMap[colCfg.key] = columnRoute;
2648
- columnCfgs.push(colCfg);
2649
- columnInfos.push({
2650
- ...colCfg,
2651
- alias: `${colCfg.key}`,
2652
- route: columnRoute,
2653
- titleShort: colCfg.key,
2654
- titleLong: colCfg.key
2655
- });
3134
+ row.push(column);
3135
+ }
3136
+ rows[sliceId] = row;
2656
3137
  }
2657
- return { columnCfgs, columnInfos, objectMap };
3138
+ return new Join(rows, columnSelection);
2658
3139
  }
2659
3140
  // ...........................................................................
2660
- /**
2661
- * Fetches data for the given ColumnSelection
2662
- * @param columnSelection - The ColumnSelection to fetch data for
2663
- */
2664
- async _getBaseDataForColumnSelection(columnSelection) {
2665
- const uniqueComponentRoutes = /* @__PURE__ */ new Set();
2666
- for (const colInfo of columnSelection.columns) {
2667
- const componentRoute = Route.fromFlat(colInfo.route);
2668
- const isolatedComponentRoute = await this.isolatePropertyKeyFromRoute(
2669
- componentRoute
2670
- );
2671
- uniqueComponentRoutes.add(
2672
- isolatedComponentRoute.toRouteWithoutProperty().flat
3141
+ async _resolveSliceIds(sliceIdTable, sliceIdRow) {
3142
+ const sliceIdController = new SliceIdController(this.core, sliceIdTable);
3143
+ sliceIdController.init();
3144
+ const resolvedSliceIds = /* @__PURE__ */ new Set();
3145
+ const {
3146
+ [sliceIdTable]: { _data: sliceIds }
3147
+ } = await sliceIdController.get(sliceIdRow);
3148
+ for (const sliceId of sliceIds) {
3149
+ const baseSliceIds = await sliceIdController.resolveBaseSliceIds(
3150
+ sliceId
2673
3151
  );
3152
+ for (const sId of baseSliceIds.add) {
3153
+ resolvedSliceIds.add(sId);
3154
+ }
2674
3155
  }
2675
- const data = {};
2676
- for (const compRouteFlat of uniqueComponentRoutes) {
2677
- const uniqueComponentRoute = Route.fromFlat(compRouteFlat);
2678
- const componentData = await this.get(uniqueComponentRoute, {});
2679
- Object.assign(data, componentData);
2680
- }
2681
- return data;
3156
+ return Array.from(resolvedSliceIds);
2682
3157
  }
2683
3158
  // ...........................................................................
2684
3159
  /**
@@ -2687,17 +3162,15 @@ class Db {
2687
3162
  * @returns The result of the Insert as an InsertHistoryRow
2688
3163
  * @throws {Error} If the Insert is not valid or if any controller cannot be created
2689
3164
  */
2690
- async insert(insert, options) {
2691
- const initialRoute = Route.fromFlat(insert.route);
2692
- const runs = await this._resolveInsert(insert);
2693
- const errors = validateInsert(insert);
2694
- if (!!errors.hasErrors) {
2695
- throw new Error(
2696
- `Db.insert: Insert is not valid:
2697
- ${JSON.stringify(errors, null, 2)}`
2698
- );
3165
+ async insert(route, tree, options) {
3166
+ const controllers = await this.indexedControllers(
3167
+ Route.fromFlat(route.flatWithoutRefs)
3168
+ );
3169
+ const runFns = {};
3170
+ for (const [tableKey, controller] of Object.entries(controllers)) {
3171
+ runFns[tableKey] = controller.insert.bind(controller);
2699
3172
  }
2700
- return this._insert(insert, initialRoute, runs, options);
3173
+ return this._insert(route, tree, runFns, options);
2701
3174
  }
2702
3175
  // ...........................................................................
2703
3176
  /**
@@ -2708,87 +3181,190 @@ ${JSON.stringify(errors, null, 2)}`
2708
3181
  * @returns The result of the Insert
2709
3182
  * @throws {Error} If the route is not valid or if any controller cannot be created
2710
3183
  */
2711
- async _insert(insert, route, runFns, options) {
2712
- let results;
2713
- let tableKey;
2714
- const segment = route.segment(0);
2715
- tableKey = segment.tableKey;
2716
- let previous = [];
2717
- if (Route.segmentHasRef(segment)) {
2718
- const routeRef = Route.segmentRef(segment);
2719
- if (Route.segmentHasInsertHistoryRef(segment)) {
2720
- previous = [...previous, routeRef];
2721
- }
2722
- if (Route.segmentHasDefaultRef(segment)) {
2723
- const timeIds = await this.getTimeIdsForRef(
2724
- tableKey,
2725
- Route.segmentRef(segment)
2726
- );
2727
- previous = [...previous, ...timeIds];
2728
- }
3184
+ async _insert(route, tree, runFns, options) {
3185
+ const results = [];
3186
+ const nodeRoute = route;
3187
+ const nodeSegment = nodeRoute.segment(0);
3188
+ const nodeTableKey = nodeSegment.tableKey;
3189
+ const nodeTree = tree[nodeTableKey];
3190
+ const nodeType = nodeTree._type;
3191
+ if (nodeTree._data.length === 0) {
3192
+ throw new Error(
3193
+ `Db._insert: No data found for table "${nodeTableKey}" in route "${route.flat}".`
3194
+ );
2729
3195
  }
2730
- if (!route.isRoot) {
2731
- const childRoute = route.deeper(1);
2732
- const childKeys = this._childKeys(insert.value);
2733
- const childRefs = {};
2734
- for (const k of childKeys) {
2735
- const childValue = insert.value[k];
2736
- const childInsert = { ...insert, value: childValue };
3196
+ const previousHash = nodeSegment[nodeTableKey + "Ref"] ?? null;
3197
+ const previousTimeId = nodeSegment[nodeTableKey + "InsertHistoryRef"] ?? null;
3198
+ const previous = previousHash ? await this.getTimeIdsForRef(nodeTableKey, previousHash) : previousTimeId ? [previousTimeId] : [];
3199
+ if (!nodeRoute.isRoot) {
3200
+ const childRoute = nodeRoute.deeper(1);
3201
+ const childTableKey = childRoute.top.tableKey;
3202
+ if (nodeType === "cakes") {
3203
+ const cakes = nodeTree._data;
3204
+ if (cakes.length > 1) ;
3205
+ const cake = cakes[0];
3206
+ const childTree = cake.layers[childTableKey];
2737
3207
  const childResults = await this._insert(
2738
- childInsert,
2739
3208
  childRoute,
3209
+ { [childTableKey]: childTree },
2740
3210
  runFns
2741
3211
  );
2742
- const childRefKey = childRoute.top.tableKey + "Ref";
2743
- const childRefArray = childResults.map(
2744
- (childResult) => childResult[childRefKey]
3212
+ if (childResults.length > 1) {
3213
+ throw new Error(
3214
+ `Db._insert: Multiple inserts returned for child table "${childTableKey}" when inserting into cake table "${nodeTableKey}". Only single child inserts are supported.`
3215
+ );
3216
+ }
3217
+ const childResult = childResults[0];
3218
+ const insertValue = {
3219
+ ...cake,
3220
+ ...{
3221
+ layers: {
3222
+ ...cake.layers,
3223
+ ...{
3224
+ [childTableKey]: childResult[childTableKey + "Ref"]
3225
+ }
3226
+ }
3227
+ }
3228
+ };
3229
+ const runFn = runFns[nodeTableKey];
3230
+ const result = await runFn("add", rmhsh(insertValue), "db.insert");
3231
+ results.push(
3232
+ ...result.map((r) => ({
3233
+ ...r,
3234
+ ...{ previous },
3235
+ ...{ route: route.flat }
3236
+ }))
2745
3237
  );
2746
- childRefs[k] = childRefArray;
2747
3238
  }
2748
- const runFn = runFns[tableKey];
2749
- results = [
2750
- ...(await runFn(
2751
- insert.command,
2752
- {
2753
- ...insert.value,
2754
- ...childRefs
2755
- },
2756
- insert.origin
2757
- )).map((r) => ({ ...r, ...{ previous } }))
2758
- ];
3239
+ if (nodeType === "layers") {
3240
+ const layers = nodeTree._data;
3241
+ for (const layer of layers) {
3242
+ const layerInsert = {};
3243
+ for (const [sliceId, componentTree] of Object.entries(layer.add)) {
3244
+ if (sliceId === "_hash") continue;
3245
+ const writtenComponents = await this._insert(
3246
+ childRoute,
3247
+ componentTree,
3248
+ runFns
3249
+ );
3250
+ if (writtenComponents.length > 1) {
3251
+ throw new Error(
3252
+ `Db._insert: Multiple components written for layer "${layer._hash}" and sliceId "${sliceId}" is currently not supported.`
3253
+ );
3254
+ }
3255
+ const writtenComponent = writtenComponents[0];
3256
+ if (!writtenComponent || !writtenComponent[childTableKey + "Ref"]) {
3257
+ throw new Error(
3258
+ `Db._insert: No component reference returned for layer "${layer._hash}" and sliceId "${sliceId}".`
3259
+ );
3260
+ }
3261
+ layerInsert[sliceId] = writtenComponent[childTableKey + "Ref"];
3262
+ }
3263
+ const runFn = runFns[nodeTableKey];
3264
+ const result = await runFn(
3265
+ "add",
3266
+ rmhsh({
3267
+ ...layer,
3268
+ ...{ add: layerInsert }
3269
+ }),
3270
+ "db.insert"
3271
+ );
3272
+ results.push(
3273
+ ...result.map((r) => ({
3274
+ ...r,
3275
+ ...{ previous },
3276
+ ...{ route: route.flat }
3277
+ }))
3278
+ );
3279
+ }
3280
+ }
3281
+ if (["components", "edits", "multiEdits"].includes(
3282
+ nodeType
3283
+ )) {
3284
+ const runFn = runFns[nodeTableKey];
3285
+ const components = nodeTree._data;
3286
+ for (const component of components) {
3287
+ const resolvedComponent = { ...component };
3288
+ for (const [property, value] of Object.entries(component)) {
3289
+ if (value.hasOwnProperty("_tableKey") && value._tableKey === childTableKey) {
3290
+ const writtenReferences = await this._insert(
3291
+ childRoute,
3292
+ { [childTableKey]: value },
3293
+ runFns
3294
+ );
3295
+ resolvedComponent[property] = writtenReferences.map(
3296
+ (wr) => wr[childTableKey + "Ref"]
3297
+ );
3298
+ }
3299
+ }
3300
+ const result = await runFn(
3301
+ "add",
3302
+ rmhsh(resolvedComponent),
3303
+ "db.insert"
3304
+ );
3305
+ results.push(
3306
+ ...result.map((r) => ({
3307
+ ...r,
3308
+ ...{ previous },
3309
+ ...{ route: route.flat }
3310
+ }))
3311
+ );
3312
+ }
3313
+ }
2759
3314
  } else {
2760
- tableKey = route.root.tableKey;
2761
- const runFn = runFns[tableKey];
2762
- const insertValue = insert.value;
2763
- for (const [propertyKey, propertyValue] of Object.entries(insert.value)) {
2764
- if (propertyValue && typeof propertyValue === "object" && !!propertyValue._tableKey) {
2765
- const referenceRoute = propertyValue._tableKey;
2766
- delete propertyValue._tableKey;
2767
- const referenceInsert = {
2768
- command: insert.command,
2769
- route: referenceRoute,
2770
- value: propertyValue
2771
- };
2772
- const referencesWritten = (await this._insert(
2773
- referenceInsert,
2774
- Route.fromFlat(referenceRoute),
2775
- runFns
2776
- )).map((h) => h[referenceRoute + "Ref"]);
2777
- insertValue[propertyKey] = referencesWritten.length === 1 ? referencesWritten[0] : referencesWritten;
3315
+ const runFn = runFns[nodeTableKey];
3316
+ if (["components", "edits", "multiEdits"].includes(
3317
+ nodeType
3318
+ )) {
3319
+ const components = rmhsh(
3320
+ tree[nodeTableKey]
3321
+ );
3322
+ for (const component of components._data) {
3323
+ if (!component) continue;
3324
+ delete component._tableKey;
3325
+ delete component._type;
3326
+ const result = await runFn("add", component, "db.insert");
3327
+ results.push(
3328
+ ...result.map((r) => ({
3329
+ ...r,
3330
+ ...{ previous },
3331
+ ...{ route: route.flat }
3332
+ }))
3333
+ );
3334
+ }
3335
+ }
3336
+ if (nodeType === "layers") {
3337
+ const layers = rmhsh(tree[nodeTableKey]);
3338
+ for (const layer of layers._data) {
3339
+ const result = await runFn("add", layer, "db.insert");
3340
+ results.push(
3341
+ ...result.map((r) => ({
3342
+ ...r,
3343
+ ...{ previous },
3344
+ ...{ route: route.flat }
3345
+ }))
3346
+ );
3347
+ }
3348
+ }
3349
+ if (nodeType === "cakes") {
3350
+ const cakes = rmhsh(tree[nodeTableKey]);
3351
+ for (const cake of cakes._data) {
3352
+ const result = await runFn("add", cake, "db.insert");
3353
+ results.push(
3354
+ ...result.map((r) => ({
3355
+ ...r,
3356
+ ...{ previous },
3357
+ ...{ route: route.flat }
3358
+ }))
3359
+ );
2778
3360
  }
2779
3361
  }
2780
- results = [
2781
- ...(await runFn(insert.command, insertValue, insert.origin)).map(
2782
- (r) => ({ ...r, previous })
2783
- )
2784
- ];
2785
3362
  }
2786
3363
  for (const result of results) {
2787
- result.route = insert.route;
2788
3364
  if (!options?.skipHistory)
2789
- await this._writeInsertHistory(tableKey, result);
3365
+ await this._writeInsertHistory(nodeTableKey, result);
2790
3366
  if (!options?.skipNotification)
2791
- this.notify.notify(Route.fromFlat(insert.route), result);
3367
+ this.notify.notify(Route.fromFlat(result.route), result);
2792
3368
  }
2793
3369
  return results;
2794
3370
  }
@@ -2811,48 +3387,6 @@ ${JSON.stringify(errors, null, 2)}`
2811
3387
  this.notify.unregister(route, callback);
2812
3388
  }
2813
3389
  // ...........................................................................
2814
- /**
2815
- * Resolves an Insert by returning the run functions of all controllers involved in the Insert's route
2816
- * @param Insert - The Insert to resolve
2817
- * @returns A record of controller run functions, keyed by table name
2818
- * @throws {Error} If the route is not valid or if any controller cannot be created
2819
- */
2820
- async _resolveInsert(Insert2) {
2821
- const controllers = await this._indexedControllers(
2822
- Route.fromFlat(Insert2.route)
2823
- );
2824
- const referencedComponentTableKeys = /* @__PURE__ */ new Set();
2825
- traverse(Insert2.value, ({ key, parent }) => {
2826
- if (key == "_tableKey")
2827
- referencedComponentTableKeys.add(parent[key]);
2828
- });
2829
- for (const tableKey of referencedComponentTableKeys) {
2830
- controllers[tableKey] ??= await this.getController(tableKey);
2831
- }
2832
- const runFns = {};
2833
- for (const tableKey of Object.keys(controllers)) {
2834
- runFns[tableKey] = controllers[tableKey].insert.bind(
2835
- controllers[tableKey]
2836
- );
2837
- }
2838
- return runFns;
2839
- }
2840
- // ...........................................................................
2841
- /**
2842
- * Returns the keys of child refs in a value based on a route
2843
- * @param value - The value to check
2844
- * @returns An array of keys of child refs in the value
2845
- */
2846
- _childKeys(value) {
2847
- const keys = Object.keys(value);
2848
- const childKeys = [];
2849
- for (const k of keys) {
2850
- if (typeof value[k] !== "object") continue;
2851
- childKeys.push(k);
2852
- }
2853
- return childKeys;
2854
- }
2855
- // ...........................................................................
2856
3390
  /**
2857
3391
  * Get a controller for a specific table
2858
3392
  * @param tableKey - The key of the table to get the controller for
@@ -2869,7 +3403,7 @@ ${JSON.stringify(errors, null, 2)}`
2869
3403
  return createController(contentType, this.core, tableKey, refs);
2870
3404
  }
2871
3405
  // ...........................................................................
2872
- async _indexedControllers(route) {
3406
+ async indexedControllers(route) {
2873
3407
  const controllers = {};
2874
3408
  const isolatedRoute = await this.isolatePropertyKeyFromRoute(route);
2875
3409
  for (let i = 0; i < isolatedRoute.segments.length; i++) {