@anchrd/intel-ui 0.2.0 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anchrd/intel-ui",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -45,6 +45,10 @@ const DeleteResult = z.strictObject({ deleted: z.boolean() });
45
45
  // Attachment uploads and downloads share this budget, so it is generous rather than snappy.
46
46
  const RequestTimeoutMs = 60_000;
47
47
 
48
+ // Deeper nesting than this is not a tree anyone navigates; the limit exists to bound the request
49
+ // fan-out on first paint, not to express a product rule.
50
+ const MaxTreeDepth = 12;
51
+
48
52
  // The Worker serves the login route, so the path belongs to the data layer rather than to any view.
49
53
  export function loginPath(returnTo?: string): string {
50
54
  return returnTo ? `/auth/login?returnTo=${encodeURIComponent(returnTo)}` : "/auth/login";
@@ -111,12 +115,23 @@ export function createIntelDataProvider(
111
115
  return await request(`/knowledge?${params}`, KnowledgeNodeList);
112
116
  }
113
117
 
114
- async function loadChildren(parentId: string | null): Promise<KnowledgeTreeNode[]> {
118
+ // One request per folder, so a deep tree is an N+1 on the first paint. The depth limit bounds that
119
+ // and, more importantly, makes a cyclic parentId impossible to hang on: without it a cycle would
120
+ // recurse until the tab dies. `seen` stops a cycle at the first repeat rather than at the limit.
121
+ async function loadChildren(
122
+ parentId: string | null,
123
+ depth = 0,
124
+ seen: ReadonlySet<string> = new Set(),
125
+ ): Promise<KnowledgeTreeNode[]> {
126
+ if (depth >= MaxTreeDepth) return [];
115
127
  const { items } = await listKnowledge({ parentId });
116
128
  return await Promise.all(
117
129
  items.map(async (node) => ({
118
130
  ...node,
119
- children: node.kind === "folder" ? await loadChildren(node.id) : [],
131
+ children:
132
+ node.kind === "folder" && !seen.has(node.id)
133
+ ? await loadChildren(node.id, depth + 1, new Set([...seen, node.id]))
134
+ : [],
120
135
  })),
121
136
  );
122
137
  }