@rljson/db 0.0.12 → 0.0.13

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.
@@ -6,9 +6,58 @@
6
6
 
7
7
  ## Table of contents <!-- omit in toc -->
8
8
 
9
+ - [Tree INSERT operations failing with empty results](#tree-insert-operations-failing-with-empty-results)
9
10
  - [Vscode Windows: Debugging is not working](#vscode-windows-debugging-is-not-working)
10
11
  - [GitHub actions: Cannot find module @rollup/rollup-linux-x64-gnu](#github-actions-cannot-find-module-rolluprollup-linux-x64-gnu)
11
12
 
13
+ ## Tree INSERT operations failing with empty results
14
+
15
+ Date: 2026-01-28
16
+
17
+ ### Symptoms
18
+
19
+ - Tree INSERT operations complete without errors
20
+ - GET queries after INSERT return empty results or only root node
21
+ - Cell length is 1 instead of expected count
22
+ - Navigation stops at root level
23
+
24
+ ### Root Cause
25
+
26
+ The `treeFromObject` function from `@rljson/rljson` v0.0.74+ creates an explicit root node with `id='root'` at the end of the tree array. When inserting an already-isolated subtree (from `isolate()`), this created a double-root structure:
27
+
28
+ ```
29
+ Auto-root (id='root')
30
+ └─ User-root (id='root')
31
+ └─ actual data nodes
32
+ ```
33
+
34
+ When `TreeController` navigates the tree, it stops at the first node with `id='root'` (the auto-root), which has no meaningful data.
35
+
36
+ ### Solution
37
+
38
+ ✅ **Fixed in current version**: The `db.ts` file now calls `treeFromObject` with `skipRootCreation: true` parameter:
39
+
40
+ ```typescript
41
+ const trees = treeFromObject(treeObject, true);
42
+ ```
43
+
44
+ This prevents the automatic root wrapper from being created during INSERT operations.
45
+
46
+ ### Verification
47
+
48
+ Run the tree INSERT tests:
49
+
50
+ ```bash
51
+ pnpm test --run --grep "insert on tree"
52
+ ```
53
+
54
+ All tree INSERT tests should pass:
55
+
56
+ - "insert on tree root node"
57
+ - "insert on tree deeper leaf"
58
+ - "insert on tree simple branch"
59
+ - "insert new child on branch"
60
+
12
61
  ## Vscode Windows: Debugging is not working
13
62
 
14
63
  Date: 2025-03-08
package/dist/db.js CHANGED
@@ -984,6 +984,15 @@ class TreeController extends BaseController {
984
984
  if (treeId && treeId !== tree.id) {
985
985
  return { [this._tableKey]: { _data: [], _type: "trees" } };
986
986
  }
987
+ const shouldExpandChildren = path !== void 0;
988
+ if (!shouldExpandChildren) {
989
+ return {
990
+ [this._tableKey]: {
991
+ _data: [tree],
992
+ _type: "trees"
993
+ }
994
+ };
995
+ }
987
996
  const children = [];
988
997
  for (const childRef of tree.children ?? []) {
989
998
  const child = await this.get(
@@ -1005,21 +1014,47 @@ class TreeController extends BaseController {
1005
1014
  if (trees.length === 0) {
1006
1015
  return {};
1007
1016
  }
1017
+ if (trees.length > 1e5) {
1018
+ throw new Error(
1019
+ `TreeController.buildTreeFromTrees: Tree size exceeds limit (${trees.length} > 100000 nodes). This may indicate a performance issue or data structure problem.`
1020
+ );
1021
+ }
1008
1022
  const treeMap = /* @__PURE__ */ new Map();
1009
1023
  for (const tree of trees) {
1010
1024
  treeMap.set(tree._hash, tree);
1011
1025
  }
1012
- const buildObject = (tree) => {
1026
+ const memo = /* @__PURE__ */ new Map();
1027
+ let buildObjectCallCount = 0;
1028
+ const MAX_ITERATIONS = 1e6;
1029
+ const buildObject = (tree, depth = 0) => {
1030
+ buildObjectCallCount++;
1031
+ if (buildObjectCallCount > MAX_ITERATIONS) {
1032
+ throw new Error(
1033
+ `TreeController.buildTreeFromTrees: Maximum iterations (${MAX_ITERATIONS}) exceeded. This likely indicates a bug. Processed ${buildObjectCallCount} nodes from ${trees.length} total.`
1034
+ );
1035
+ }
1036
+ if (depth > 1e4) {
1037
+ throw new Error(
1038
+ `TreeController.buildTreeFromTrees: Tree depth exceeds limit (${depth} > 10000). This may indicate a circular reference or extremely deep structure.`
1039
+ );
1040
+ }
1041
+ const hash = tree._hash;
1042
+ if (memo.has(hash)) {
1043
+ return memo.get(hash);
1044
+ }
1013
1045
  if (!tree.isParent || !tree.children || tree.children.length === 0) {
1014
- return tree;
1046
+ const result3 = tree;
1047
+ memo.set(hash, result3);
1048
+ return result3;
1015
1049
  }
1016
1050
  const result2 = {};
1017
1051
  for (const childHash of tree.children) {
1018
1052
  const childTree = treeMap.get(childHash);
1019
1053
  if (childTree && childTree.id) {
1020
- result2[childTree.id] = buildObject(childTree);
1054
+ result2[childTree.id] = buildObject(childTree, depth + 1);
1021
1055
  }
1022
1056
  }
1057
+ memo.set(hash, result2);
1023
1058
  return result2;
1024
1059
  };
1025
1060
  const referencedHashes = /* @__PURE__ */ new Set();
@@ -1105,6 +1140,9 @@ class TreeController extends BaseController {
1105
1140
  return cells;
1106
1141
  }
1107
1142
  async getChildRefs(where, filter) {
1143
+ if (typeof where === "string") {
1144
+ return [];
1145
+ }
1108
1146
  const childRefs = [];
1109
1147
  const { [this._tableKey]: table } = await this.get(where, filter);
1110
1148
  const trees = table._data;
@@ -1241,7 +1279,11 @@ class Core {
1241
1279
  // ...........................................................................
1242
1280
  /** Reads a specific row from a database table */
1243
1281
  async readRow(table, rowHash) {
1244
- return await this._io.readRows({ table, where: { _hash: rowHash } });
1282
+ const result = await this._io.readRows({
1283
+ table,
1284
+ where: { _hash: rowHash }
1285
+ });
1286
+ return result;
1245
1287
  }
1246
1288
  // ...........................................................................
1247
1289
  async readRows(table, where) {
@@ -2919,6 +2961,12 @@ class Db {
2919
2961
  * @returns - An Rljson object matching the route and filters
2920
2962
  */
2921
2963
  async _get(route, where, controllers, filter, sliceIds, routeAccumulator, options) {
2964
+ const depth = routeAccumulator ? routeAccumulator.flat.split("/").length : 1;
2965
+ if (depth > 100) {
2966
+ throw new Error(
2967
+ `Db._get: Maximum recursion depth (100) exceeded. This likely indicates a bug in route resolution or where clause construction. Current route: ${route.flat}, routeAccumulator: ${routeAccumulator?.flat}`
2968
+ );
2969
+ }
2922
2970
  const opts = options ?? {};
2923
2971
  const routeHasRefs = route.flat != route.flatWithoutRefs;
2924
2972
  const hasFilter = filter !== void 0 && filter.length > 0;
@@ -3738,7 +3786,7 @@ class Db {
3738
3786
  `Db._insert: No tree data found for table "${nodeTableKey}" in route "${route.flat}".`
3739
3787
  );
3740
3788
  }
3741
- const trees = treeFromObject(treeObject);
3789
+ const trees = treeFromObject(treeObject, true);
3742
3790
  const writePromises = trees.map(
3743
3791
  (tree2) => runFn("add", tree2, "db.insert")
3744
3792
  );
@@ -3862,9 +3910,7 @@ class Db {
3862
3910
  const multiEditController = await this.getController(
3863
3911
  cakeKey + "MultiEdits"
3864
3912
  );
3865
- const { [cakeKey + "MultiEdits"]: result } = await multiEditController.get(
3866
- where
3867
- );
3913
+ const { [cakeKey + "MultiEdits"]: result } = await multiEditController.get(where);
3868
3914
  return result._data;
3869
3915
  }
3870
3916
  // ...........................................................................
@@ -6091,6 +6137,10 @@ export {
6091
6137
  exampleEditActionRowSort,
6092
6138
  exampleEditActionSetValue,
6093
6139
  exampleEditSetValueReferenced,
6140
+ inject,
6141
+ isolate,
6142
+ makeUnique,
6143
+ mergeTrees,
6094
6144
  staticExample
6095
6145
  };
6096
6146
  //# sourceMappingURL=db.js.map