@i4e/invest4edu-access-core 0.2.0 → 0.3.0

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,13 +1,14 @@
1
1
  {
2
2
  "name": "@i4e/invest4edu-access-core",
3
- "version": "0.2.0",
4
- "description": "Shared tenant-isolation / access-control primitives (Track 3 D1 keystone + role capabilities) for NeoFindesk backends.",
3
+ "version": "0.3.0",
4
+ "description": "Shared tenant-isolation / access-control primitives (Track 3 D1 keystone + role capabilities + reportee tree) for NeoFindesk backends.",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./src/index.js",
8
8
  "./tenant-context": "./src/tenant-context.js",
9
9
  "./tenant-plugin": "./src/tenant-plugin.js",
10
- "./role-capabilities": "./src/role-capabilities.js"
10
+ "./role-capabilities": "./src/role-capabilities.js",
11
+ "./reportee-tree": "./src/reportee-tree.js"
11
12
  },
12
13
  "files": [
13
14
  "src",
package/src/index.js CHANGED
@@ -10,3 +10,4 @@ export {
10
10
  buildCapabilityMap,
11
11
  resolveCapabilities,
12
12
  } from "./role-capabilities.js";
13
+ export { resolveReporteeUserIds } from "./reportee-tree.js";
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Reportee-tree DFS — @i4e/invest4edu-access-core (Track 3 G3 shared primitive).
3
+ *
4
+ * The one canonical "self + all reportees" traversal, shared by both backends so v1 and v2
5
+ * compose the SAME hierarchy scope. Pure + data-source agnostic: the caller passes a flat
6
+ * employees list `[{ user_id, reporting_manager }]` (v1 feeds the Redis snapshot, v2 the
7
+ * `employees` collection) and one-or-more root user ids (delegation passes `[self, ...givers]`).
8
+ *
9
+ * @param {string|string[]} roots root user id(s)
10
+ * @param {Array<{user_id:*, reporting_manager:*}>} employees flat employee list
11
+ * @returns {string[]} de-duplicated reachable set (roots + all reportees) as STRING ids
12
+ */
13
+ export function resolveReporteeUserIds(roots, employees) {
14
+ const rootIds = (Array.isArray(roots) ? roots : [roots]).filter((id) => id != null).map(String);
15
+
16
+ const byManager = new Map();
17
+ for (const emp of employees || []) {
18
+ if (!emp || !emp.user_id) continue;
19
+ const managerId = String(emp.reporting_manager);
20
+ if (!byManager.has(managerId)) byManager.set(managerId, []);
21
+ byManager.get(managerId).push(String(emp.user_id));
22
+ }
23
+
24
+ const visited = new Set();
25
+ const result = new Set();
26
+ const stack = [...rootIds];
27
+ while (stack.length) {
28
+ const id = stack.pop();
29
+ if (!id || visited.has(id)) continue;
30
+ visited.add(id);
31
+ result.add(id);
32
+ for (const rep of byManager.get(id) || []) stack.push(rep);
33
+ }
34
+ return [...result];
35
+ }