@i4e/invest4edu-access-core 0.8.0 → 0.9.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 +1 -1
- package/src/access-resolver.js +7 -1
- package/src/access-schema.js +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i4e/invest4edu-access-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Shared access-control primitives for NeoFindesk: tenant keystone, role capabilities, reportee tree, feature flags, and the unified access engine (registry schema, snapshot resolver, visibleWhen).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
package/src/access-resolver.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Design: TDD-unified-access-and-subscription.md §4
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { parentCodeOf } from "./access-schema.js";
|
|
15
|
+
import { parentCodeOf, PRODUCT_UI_MODES } from "./access-schema.js";
|
|
16
16
|
|
|
17
17
|
/** Reasons a feature can be locked, surfaced so the UI can distinguish upsell from hard-deny. */
|
|
18
18
|
export const ENTITLEMENT_REASONS = Object.freeze({
|
|
@@ -219,6 +219,12 @@ export function resolveAccessSnapshot({
|
|
|
219
219
|
actions: [],
|
|
220
220
|
entitlement,
|
|
221
221
|
tracking: !!f.tracking_enabled,
|
|
222
|
+
// How this SCREEN should present product-keyed things it renders — distinct from
|
|
223
|
+
// `products` above, which is *reachability* ("this screen exists only for these
|
|
224
|
+
// products"). The two are independent: Open Orders is product-agnostic yet renders
|
|
225
|
+
// product tabs that must be filtered, so the behaviour cannot be derived from the
|
|
226
|
+
// constraint. Default 'none' keeps every existing screen rendering exactly as today.
|
|
227
|
+
product_ui: PRODUCT_UI_MODES.includes(f.product_ui) ? f.product_ui : "none",
|
|
222
228
|
};
|
|
223
229
|
|
|
224
230
|
if (f.feature_type === "screen") allowedScreens.push(f);
|
package/src/access-schema.js
CHANGED
|
@@ -23,6 +23,27 @@ export const REGISTRY_STATUSES = Object.freeze(["active", "dormant", "retired"])
|
|
|
23
23
|
/** Per-feature enforcement ladder (mirrors the platform's off→warn→enforce doctrine). */
|
|
24
24
|
export const ROLLOUT_MODES = Object.freeze(["off", "warn", "enforce"]);
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* How a SCREEN presents the product-keyed things it renders — product tiles, product tabs, the
|
|
28
|
+
* product column on Add Order, scheme lists.
|
|
29
|
+
*
|
|
30
|
+
* Deliberately separate from `products[]` on the same feature, which is *reachability* ("this
|
|
31
|
+
* screen exists only for these products"). The two are independent and neither implies the other:
|
|
32
|
+
* Open Orders is product-agnostic (`products: []`, it serves every product) yet renders product
|
|
33
|
+
* tabs that must be filtered; Product Setup is equally agnostic and should hide nothing. Same
|
|
34
|
+
* reachability, opposite presentation — so this cannot be derived, it has to be declared.
|
|
35
|
+
*
|
|
36
|
+
* none render everything. The default, so every existing screen is unchanged.
|
|
37
|
+
* filter hide product-keyed items outside the caller's scope. For transactional screens.
|
|
38
|
+
* readonly show everything, but out-of-scope items are not interactive. For configuration
|
|
39
|
+
* screens, where hiding a product reads as "the catalogue is broken" rather than as
|
|
40
|
+
* "you may not touch this".
|
|
41
|
+
*
|
|
42
|
+
* This is presentation only. The server-side gate is what actually refuses a write — hiding a
|
|
43
|
+
* tile has never stopped a POST.
|
|
44
|
+
*/
|
|
45
|
+
export const PRODUCT_UI_MODES = Object.freeze(["none", "filter", "readonly"]);
|
|
46
|
+
|
|
26
47
|
/** Grant effect. `deny` at the user level replaces the old `revoked_pages`. */
|
|
27
48
|
export const GRANT_EFFECTS = Object.freeze(["allow", "deny"]);
|
|
28
49
|
|
|
@@ -96,6 +117,7 @@ export const ACCESS_FEATURE_DEF = Object.freeze({
|
|
|
96
117
|
status: { type: "String", enum: REGISTRY_STATUSES, default: "active", index: true },
|
|
97
118
|
kill_switch: { type: "String", enum: ["on", "off"], default: "off" },
|
|
98
119
|
rollout_mode: { type: "String", enum: ROLLOUT_MODES, default: "off" },
|
|
120
|
+
product_ui: { type: "String", enum: PRODUCT_UI_MODES, default: "none" },
|
|
99
121
|
|
|
100
122
|
// Survives an entitlement block (login, billing/checkout, profile) — see design §7.4.
|
|
101
123
|
always_available: { type: "Boolean", default: false },
|
|
@@ -139,6 +161,7 @@ export default {
|
|
|
139
161
|
FEATURE_TYPES,
|
|
140
162
|
REGISTRY_STATUSES,
|
|
141
163
|
ROLLOUT_MODES,
|
|
164
|
+
PRODUCT_UI_MODES,
|
|
142
165
|
GRANT_EFFECTS,
|
|
143
166
|
GRANT_SUBJECTS,
|
|
144
167
|
FEATURE_CODE_PATTERN,
|