@ultimat3/admin 27.2.0 → 27.2.2

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.
Files changed (2) hide show
  1. package/package.json +17 -17
  2. package/src/policy-bridge.ts +21 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/admin",
3
- "version": "27.2.0",
3
+ "version": "27.2.2",
4
4
  "description": "Two dashboards: the /_x framework dev panels and the generated, AI-first app admin",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,21 +34,21 @@
34
34
  "test": "bun test"
35
35
  },
36
36
  "dependencies": {
37
- "@ultimat3/action": "27.2.0",
38
- "@ultimat3/ai": "27.2.0",
39
- "@ultimat3/cache": "27.2.0",
40
- "@ultimat3/core": "27.2.0",
41
- "@ultimat3/db": "27.2.0",
42
- "@ultimat3/entity": "27.2.0",
43
- "@ultimat3/http": "27.2.0",
44
- "@ultimat3/i18n": "27.2.0",
45
- "@ultimat3/jobs": "27.2.0",
46
- "@ultimat3/mcp": "27.2.0",
47
- "@ultimat3/money": "27.2.0",
48
- "@ultimat3/policy": "27.2.0",
49
- "@ultimat3/query": "27.2.0",
50
- "@ultimat3/render": "27.2.0",
51
- "@ultimat3/schema": "27.2.0",
52
- "@ultimat3/ui": "27.2.0"
37
+ "@ultimat3/action": "27.2.2",
38
+ "@ultimat3/ai": "27.2.2",
39
+ "@ultimat3/cache": "27.2.2",
40
+ "@ultimat3/core": "27.2.2",
41
+ "@ultimat3/db": "27.2.2",
42
+ "@ultimat3/entity": "27.2.2",
43
+ "@ultimat3/http": "27.2.2",
44
+ "@ultimat3/i18n": "27.2.2",
45
+ "@ultimat3/jobs": "27.2.2",
46
+ "@ultimat3/mcp": "27.2.2",
47
+ "@ultimat3/money": "27.2.2",
48
+ "@ultimat3/policy": "27.2.2",
49
+ "@ultimat3/query": "27.2.2",
50
+ "@ultimat3/render": "27.2.2",
51
+ "@ultimat3/schema": "27.2.2",
52
+ "@ultimat3/ui": "27.2.2"
53
53
  }
54
54
  }
@@ -41,6 +41,21 @@ const policyActor = (actor: AdminActor): Actor =>
41
41
  orgId: actor.orgId,
42
42
  });
43
43
 
44
+ /**
45
+ * One evaluated clause as the line `/_x` prints: ` can(job:read): allow`, indented by depth, its
46
+ * reason after a refusal. A `TraceEntry` through `String()` read `[object Object]`.
47
+ */
48
+ function traceLine(entry: unknown): string {
49
+ if (typeof entry !== 'object' || entry === null) return String(entry);
50
+ const { label, depth, allowed, reason } = entry as Partial<
51
+ Record<'label' | 'depth' | 'allowed' | 'reason', unknown>
52
+ >;
53
+ const indent = ' '.repeat(typeof depth === 'number' && depth > 0 ? depth : 0);
54
+ const verdict = allowed === true ? 'allow' : 'deny';
55
+ const why = typeof reason === 'string' && reason !== '' ? ` (${reason})` : '';
56
+ return `${indent}${String(label)}: ${verdict}${why}`;
57
+ }
58
+
44
59
  /**
45
60
  * `evaluate()`'s result is read structurally: the policy layer owns its own decision type,
46
61
  * and the admin only needs the verdict, a reason key, and the trace it prints in `/_x`.
@@ -59,12 +74,15 @@ function readDecision(permission: string, result: unknown): AdminDecision {
59
74
  };
60
75
  const verdict = result === true || bag.allowed === true;
61
76
  const reason = typeof inner.reason === 'string' ? inner.reason : 'admin.policy.evaluated';
62
- const trace = Array.isArray(bag.trace) ? bag.trace.map((line) => String(line)) : [];
77
+ const trace = Array.isArray(bag.trace) ? bag.trace.map(traceLine) : [];
63
78
  return verdict ? adminAllowed(permission, reason, trace) : adminDenied(permission, reason, trace);
64
79
  }
65
80
 
66
- /** `resource:verb` — the only shape `can()` takes. Anything else is denied, never thrown. */
67
- const isPermission = (value: string): value is Permission => /^[^:]+:[^:]+$/.test(value);
81
+ /**
82
+ * `resource:verb`, the resource itself colon-scoped or not (`admin:support:write`) — the shape
83
+ * `Permission` types and `can()` takes. Anything else is denied, never thrown.
84
+ */
85
+ const isPermission = (value: string): value is Permission => /^[^:]+(?::[^:]+)+$/.test(value);
68
86
 
69
87
  /**
70
88
  * Declare the admin's OWN permissions (`ADMIN_PERMISSIONS`) and the ones a mount DERIVES —