@headless-tree/core 0.0.0-20250506225933 → 0.0.0-20250507224143

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/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # @headless-tree/core
2
2
 
3
- ## 0.0.0-20250506225933
3
+ ## 0.0.0-20250507224143
4
+
5
+ ### Minor Changes
6
+
7
+ - 64d8e2a: add getChildrenWithData method to data loader to support fetching all children of an item at once
8
+
9
+ ## 1.0.1
4
10
 
5
11
  ### Patch Changes
6
12
 
@@ -76,11 +76,12 @@ exports.asyncDataLoaderFeature = {
76
76
  return (_d = (_c = config.createLoadingItemData) === null || _c === void 0 ? void 0 : _c.call(config)) !== null && _d !== void 0 ? _d : null;
77
77
  },
78
78
  retrieveChildrenIds: ({ tree }, itemId) => {
79
- var _a;
80
- var _b;
79
+ var _a, _b;
80
+ var _c, _d;
81
81
  const config = tree.getConfig();
82
82
  const dataRef = tree.getDataRef();
83
- (_a = (_b = dataRef.current).childrenIds) !== null && _a !== void 0 ? _a : (_b.childrenIds = {});
83
+ (_a = (_c = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_c.itemData = {});
84
+ (_b = (_d = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_d.childrenIds = {});
84
85
  if (dataRef.current.childrenIds[itemId]) {
85
86
  return dataRef.current.childrenIds[itemId];
86
87
  }
@@ -89,14 +90,31 @@ exports.asyncDataLoaderFeature = {
89
90
  }
90
91
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => [...loadingItemChildrens, itemId]);
91
92
  (() => __awaiter(void 0, void 0, void 0, function* () {
92
- var _a, _b, _c, _d;
93
- const childrenIds = yield config.dataLoader.getChildren(itemId);
94
- dataRef.current.childrenIds[itemId] = childrenIds;
95
- (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
93
+ var _a, _b, _c, _d, _e;
94
+ if ("getChildrenWithData" in config.dataLoader) {
95
+ const children = yield config.dataLoader.getChildrenWithData(itemId);
96
+ const childrenIds = children.map((c) => c.id);
97
+ dataRef.current.childrenIds[itemId] = childrenIds;
98
+ children.forEach(({ id, data }) => {
99
+ var _a, _b, _c;
100
+ dataRef.current.itemData[id] = data;
101
+ (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, id, data);
102
+ (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[id].forEach((cb) => cb());
103
+ (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[id];
104
+ });
105
+ (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
106
+ tree.rebuildTree();
107
+ tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => !childrenIds.includes(id)));
108
+ }
109
+ else {
110
+ const childrenIds = yield config.dataLoader.getChildren(itemId);
111
+ dataRef.current.childrenIds[itemId] = childrenIds;
112
+ (_b = config.onLoadedChildren) === null || _b === void 0 ? void 0 : _b.call(config, itemId, childrenIds);
113
+ tree.rebuildTree();
114
+ }
96
115
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => loadingItemChildrens.filter((id) => id !== itemId));
97
- tree.rebuildTree();
98
- (_c = (_b = dataRef.current.awaitingItemChildrensLoading) === null || _b === void 0 ? void 0 : _b[itemId]) === null || _c === void 0 ? void 0 : _c.forEach((cb) => cb());
99
- (_d = dataRef.current.awaitingItemChildrensLoading) === null || _d === void 0 ? true : delete _d[itemId];
116
+ (_d = (_c = dataRef.current.awaitingItemChildrensLoading) === null || _c === void 0 ? void 0 : _c[itemId]) === null || _d === void 0 ? void 0 : _d.forEach((cb) => cb());
117
+ (_e = dataRef.current.awaitingItemChildrensLoading) === null || _e === void 0 ? true : delete _e[itemId];
100
118
  }))();
101
119
  return [];
102
120
  },
@@ -13,6 +13,12 @@ exports.syncDataLoaderFeature = void 0;
13
13
  const utils_1 = require("../../utils");
14
14
  const errors_1 = require("../../utilities/errors");
15
15
  const promiseErrorMessage = "sync dataLoader returned promise";
16
+ const unpromise = (data) => {
17
+ if (!data || (typeof data === "object" && "then" in data)) {
18
+ throw (0, errors_1.throwError)(promiseErrorMessage);
19
+ }
20
+ return data;
21
+ };
16
22
  exports.syncDataLoaderFeature = {
17
23
  key: "sync-data-loader",
18
24
  getInitialState: (initialState) => (Object.assign({ loadingItemData: [], loadingItemChildrens: [] }, initialState)),
@@ -25,18 +31,14 @@ exports.syncDataLoaderFeature = {
25
31
  waitForItemDataLoaded: () => __awaiter(void 0, void 0, void 0, function* () { }),
26
32
  waitForItemChildrenLoaded: () => __awaiter(void 0, void 0, void 0, function* () { }),
27
33
  retrieveItemData: ({ tree }, itemId) => {
28
- const data = tree.getConfig().dataLoader.getItem(itemId);
29
- if (typeof data === "object" && "then" in data) {
30
- throw (0, errors_1.throwError)(promiseErrorMessage);
31
- }
32
- return data;
34
+ return unpromise(tree.getConfig().dataLoader.getItem(itemId));
33
35
  },
34
36
  retrieveChildrenIds: ({ tree }, itemId) => {
35
- const data = tree.getConfig().dataLoader.getChildren(itemId);
36
- if (typeof data === "object" && "then" in data) {
37
- throw (0, errors_1.throwError)(promiseErrorMessage);
37
+ const { dataLoader } = tree.getConfig();
38
+ if ("getChildren" in dataLoader) {
39
+ return unpromise(dataLoader.getChildren(itemId));
38
40
  }
39
- return data;
41
+ return unpromise(dataLoader.getChildrenWithData(itemId)).map((c) => c.data);
40
42
  },
41
43
  },
42
44
  itemInstance: {
@@ -1,7 +1,16 @@
1
- export interface TreeDataLoader<T> {
1
+ export type TreeDataLoader<T> = {
2
2
  getItem: (itemId: string) => T | Promise<T>;
3
3
  getChildren: (itemId: string) => string[] | Promise<string[]>;
4
- }
4
+ } | {
5
+ getItem: (itemId: string) => T | Promise<T>;
6
+ getChildrenWithData: (itemId: string) => {
7
+ id: string;
8
+ data: T;
9
+ }[] | Promise<{
10
+ id: string;
11
+ data: T;
12
+ }[]>;
13
+ };
5
14
  export type SyncDataLoaderFeatureDef<T> = {
6
15
  state: {};
7
16
  config: {
@@ -73,11 +73,12 @@ export const asyncDataLoaderFeature = {
73
73
  return (_d = (_c = config.createLoadingItemData) === null || _c === void 0 ? void 0 : _c.call(config)) !== null && _d !== void 0 ? _d : null;
74
74
  },
75
75
  retrieveChildrenIds: ({ tree }, itemId) => {
76
- var _a;
77
- var _b;
76
+ var _a, _b;
77
+ var _c, _d;
78
78
  const config = tree.getConfig();
79
79
  const dataRef = tree.getDataRef();
80
- (_a = (_b = dataRef.current).childrenIds) !== null && _a !== void 0 ? _a : (_b.childrenIds = {});
80
+ (_a = (_c = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_c.itemData = {});
81
+ (_b = (_d = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_d.childrenIds = {});
81
82
  if (dataRef.current.childrenIds[itemId]) {
82
83
  return dataRef.current.childrenIds[itemId];
83
84
  }
@@ -86,14 +87,31 @@ export const asyncDataLoaderFeature = {
86
87
  }
87
88
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => [...loadingItemChildrens, itemId]);
88
89
  (() => __awaiter(void 0, void 0, void 0, function* () {
89
- var _a, _b, _c, _d;
90
- const childrenIds = yield config.dataLoader.getChildren(itemId);
91
- dataRef.current.childrenIds[itemId] = childrenIds;
92
- (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
90
+ var _a, _b, _c, _d, _e;
91
+ if ("getChildrenWithData" in config.dataLoader) {
92
+ const children = yield config.dataLoader.getChildrenWithData(itemId);
93
+ const childrenIds = children.map((c) => c.id);
94
+ dataRef.current.childrenIds[itemId] = childrenIds;
95
+ children.forEach(({ id, data }) => {
96
+ var _a, _b, _c;
97
+ dataRef.current.itemData[id] = data;
98
+ (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, id, data);
99
+ (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[id].forEach((cb) => cb());
100
+ (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[id];
101
+ });
102
+ (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
103
+ tree.rebuildTree();
104
+ tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => !childrenIds.includes(id)));
105
+ }
106
+ else {
107
+ const childrenIds = yield config.dataLoader.getChildren(itemId);
108
+ dataRef.current.childrenIds[itemId] = childrenIds;
109
+ (_b = config.onLoadedChildren) === null || _b === void 0 ? void 0 : _b.call(config, itemId, childrenIds);
110
+ tree.rebuildTree();
111
+ }
93
112
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => loadingItemChildrens.filter((id) => id !== itemId));
94
- tree.rebuildTree();
95
- (_c = (_b = dataRef.current.awaitingItemChildrensLoading) === null || _b === void 0 ? void 0 : _b[itemId]) === null || _c === void 0 ? void 0 : _c.forEach((cb) => cb());
96
- (_d = dataRef.current.awaitingItemChildrensLoading) === null || _d === void 0 ? true : delete _d[itemId];
113
+ (_d = (_c = dataRef.current.awaitingItemChildrensLoading) === null || _c === void 0 ? void 0 : _c[itemId]) === null || _d === void 0 ? void 0 : _d.forEach((cb) => cb());
114
+ (_e = dataRef.current.awaitingItemChildrensLoading) === null || _e === void 0 ? true : delete _e[itemId];
97
115
  }))();
98
116
  return [];
99
117
  },
@@ -10,6 +10,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { makeStateUpdater } from "../../utils";
11
11
  import { throwError } from "../../utilities/errors";
12
12
  const promiseErrorMessage = "sync dataLoader returned promise";
13
+ const unpromise = (data) => {
14
+ if (!data || (typeof data === "object" && "then" in data)) {
15
+ throw throwError(promiseErrorMessage);
16
+ }
17
+ return data;
18
+ };
13
19
  export const syncDataLoaderFeature = {
14
20
  key: "sync-data-loader",
15
21
  getInitialState: (initialState) => (Object.assign({ loadingItemData: [], loadingItemChildrens: [] }, initialState)),
@@ -22,18 +28,14 @@ export const syncDataLoaderFeature = {
22
28
  waitForItemDataLoaded: () => __awaiter(void 0, void 0, void 0, function* () { }),
23
29
  waitForItemChildrenLoaded: () => __awaiter(void 0, void 0, void 0, function* () { }),
24
30
  retrieveItemData: ({ tree }, itemId) => {
25
- const data = tree.getConfig().dataLoader.getItem(itemId);
26
- if (typeof data === "object" && "then" in data) {
27
- throw throwError(promiseErrorMessage);
28
- }
29
- return data;
31
+ return unpromise(tree.getConfig().dataLoader.getItem(itemId));
30
32
  },
31
33
  retrieveChildrenIds: ({ tree }, itemId) => {
32
- const data = tree.getConfig().dataLoader.getChildren(itemId);
33
- if (typeof data === "object" && "then" in data) {
34
- throw throwError(promiseErrorMessage);
34
+ const { dataLoader } = tree.getConfig();
35
+ if ("getChildren" in dataLoader) {
36
+ return unpromise(dataLoader.getChildren(itemId));
35
37
  }
36
- return data;
38
+ return unpromise(dataLoader.getChildrenWithData(itemId)).map((c) => c.data);
37
39
  },
38
40
  },
39
41
  itemInstance: {
@@ -1,7 +1,16 @@
1
- export interface TreeDataLoader<T> {
1
+ export type TreeDataLoader<T> = {
2
2
  getItem: (itemId: string) => T | Promise<T>;
3
3
  getChildren: (itemId: string) => string[] | Promise<string[]>;
4
- }
4
+ } | {
5
+ getItem: (itemId: string) => T | Promise<T>;
6
+ getChildrenWithData: (itemId: string) => {
7
+ id: string;
8
+ data: T;
9
+ }[] | Promise<{
10
+ id: string;
11
+ data: T;
12
+ }[]>;
13
+ };
5
14
  export type SyncDataLoaderFeatureDef<T> = {
6
15
  state: {};
7
16
  config: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@headless-tree/core",
3
- "version": "0.0.0-20250506225933",
3
+ "version": "0.0.0-20250507224143",
4
4
  "type": "module",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/esm/index.js",
@@ -125,4 +125,6 @@ describe("core-feature/selections", () => {
125
125
  expect(getChildren).toHaveBeenCalledTimes(1);
126
126
  });
127
127
  });
128
+
129
+ // TODO - add tests for getChildrenWithData
128
130
  });
@@ -86,6 +86,7 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
86
86
  retrieveChildrenIds: ({ tree }, itemId) => {
87
87
  const config = tree.getConfig();
88
88
  const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
89
+ dataRef.current.itemData ??= {};
89
90
  dataRef.current.childrenIds ??= {};
90
91
  if (dataRef.current.childrenIds[itemId]) {
91
92
  return dataRef.current.childrenIds[itemId];
@@ -101,15 +102,34 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
101
102
  );
102
103
 
103
104
  (async () => {
104
- const childrenIds = await config.dataLoader.getChildren(itemId);
105
- dataRef.current.childrenIds[itemId] = childrenIds;
106
- config.onLoadedChildren?.(itemId, childrenIds);
105
+ if ("getChildrenWithData" in config.dataLoader) {
106
+ const children = await config.dataLoader.getChildrenWithData(itemId);
107
+ const childrenIds = children.map((c) => c.id);
108
+ dataRef.current.childrenIds[itemId] = childrenIds;
109
+ children.forEach(({ id, data }) => {
110
+ dataRef.current.itemData[id] = data;
111
+ config.onLoadedItem?.(id, data);
112
+ dataRef.current.awaitingItemDataLoading?.[id].forEach((cb) => cb());
113
+ delete dataRef.current.awaitingItemDataLoading?.[id];
114
+ });
115
+
116
+ config.onLoadedChildren?.(itemId, childrenIds);
117
+ tree.rebuildTree();
118
+ tree.applySubStateUpdate("loadingItemData", (loadingItemData) =>
119
+ loadingItemData.filter((id) => !childrenIds.includes(id)),
120
+ );
121
+ } else {
122
+ const childrenIds = await config.dataLoader.getChildren(itemId);
123
+ dataRef.current.childrenIds[itemId] = childrenIds;
124
+ config.onLoadedChildren?.(itemId, childrenIds);
125
+ tree.rebuildTree();
126
+ }
127
+
107
128
  tree.applySubStateUpdate(
108
129
  "loadingItemChildrens",
109
130
  (loadingItemChildrens) =>
110
131
  loadingItemChildrens.filter((id) => id !== itemId),
111
132
  );
112
- tree.rebuildTree();
113
133
 
114
134
  dataRef.current.awaitingItemChildrensLoading?.[itemId]?.forEach((cb) =>
115
135
  cb(),
@@ -3,6 +3,12 @@ import { makeStateUpdater } from "../../utils";
3
3
  import { throwError } from "../../utilities/errors";
4
4
 
5
5
  const promiseErrorMessage = "sync dataLoader returned promise";
6
+ const unpromise = <T>(data: T | Promise<T>): T => {
7
+ if (!data || (typeof data === "object" && "then" in data)) {
8
+ throw throwError(promiseErrorMessage);
9
+ }
10
+ return data;
11
+ };
6
12
 
7
13
  export const syncDataLoaderFeature: FeatureImplementation = {
8
14
  key: "sync-data-loader",
@@ -29,19 +35,17 @@ export const syncDataLoaderFeature: FeatureImplementation = {
29
35
  waitForItemChildrenLoaded: async () => {},
30
36
 
31
37
  retrieveItemData: ({ tree }, itemId) => {
32
- const data = tree.getConfig().dataLoader.getItem(itemId);
33
- if (typeof data === "object" && "then" in data) {
34
- throw throwError(promiseErrorMessage);
35
- }
36
- return data;
38
+ return unpromise(tree.getConfig().dataLoader.getItem(itemId));
37
39
  },
38
40
 
39
41
  retrieveChildrenIds: ({ tree }, itemId) => {
40
- const data = tree.getConfig().dataLoader.getChildren(itemId);
41
- if (typeof data === "object" && "then" in data) {
42
- throw throwError(promiseErrorMessage);
42
+ const { dataLoader } = tree.getConfig();
43
+ if ("getChildren" in dataLoader) {
44
+ return unpromise(dataLoader.getChildren(itemId));
43
45
  }
44
- return data;
46
+ return unpromise(dataLoader.getChildrenWithData(itemId)).map(
47
+ (c) => c.data,
48
+ );
45
49
  },
46
50
  },
47
51
 
@@ -1,7 +1,14 @@
1
- export interface TreeDataLoader<T> {
2
- getItem: (itemId: string) => T | Promise<T>;
3
- getChildren: (itemId: string) => string[] | Promise<string[]>;
4
- }
1
+ export type TreeDataLoader<T> =
2
+ | {
3
+ getItem: (itemId: string) => T | Promise<T>;
4
+ getChildren: (itemId: string) => string[] | Promise<string[]>;
5
+ }
6
+ | {
7
+ getItem: (itemId: string) => T | Promise<T>;
8
+ getChildrenWithData: (
9
+ itemId: string,
10
+ ) => { id: string; data: T }[] | Promise<{ id: string; data: T }[]>;
11
+ };
5
12
 
6
13
  export type SyncDataLoaderFeatureDef<T> = {
7
14
  state: {};