@stapel/workspaces-react 0.15.1 → 0.16.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/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # @stapel/workspaces-react
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e25e9a6: `useMandateState()`, and a `resolveNav` that consumes the surface axis.
8
+
9
+ `is_guest` has ridden the workspace-list response since stapel-workspaces
10
+ 0.19 and had **zero readers**. `useMandateState()` is the first one: the
11
+ single point of truth for "does this person hold a mandate anywhere",
12
+ computed from two answers that already existed — the active session's status
13
+ (which settles anonymous and no-session without asking anyone) and the
14
+ server's own `is_guest` predicate. No new endpoint, and no extra request:
15
+ it reads the same `useWorkspaces()` a screen is already running.
16
+
17
+ The server evaluates the predicate; the hook does not re-derive it. A caller
18
+ can hold membership rows that grant no mandate, so `workspaces.length` is not
19
+ the question — it is consulted only against a backend too old to answer.
20
+
21
+ The unresolved case is the reason the hook is shaped the way it is. A list in
22
+ flight and a list that 502'd both resolve to `unresolved` with a reason, and
23
+ neither ever resolves to `guest`. The one-liner this forecloses —
24
+ `data?.is_guest ?? true` — turns every backend hiccup into "you are a guest",
25
+ locks members out of their own product, and explains nothing; there is no
26
+ expression of that shape available, because the pending and failed states
27
+ carry no principal to read. Render it with `matchMandate`: a wait, or the
28
+ outage stated out loud.
29
+
30
+ `resolveNav` now takes an optional `{ audience }` and every `ResolvedNavEntry`
31
+ carries its resolved `surface`. Omit the audience and nothing changes — the
32
+ scaffold-codegen call site keeps baking every route, and so does every
33
+ existing runtime caller. Pass one and a screen closed to that principal is
34
+ dropped, menu entry and route together, which is the fix: the tree a host
35
+ mounts from is the tree the axis filtered. A project's override file can flip
36
+ `menuVisible` and `order`; it deliberately cannot flip this, because a
37
+ per-project preference must not put a screen that will refuse the caller back
38
+ in front of them.
39
+
40
+ `audience` is a `MandatePrincipal`, so `"unresolved"` cannot be passed. A host
41
+ whose mandate has not settled has to render the wait or the error rather than
42
+ resolve a nav for it — the alternative is a menu that quietly empties itself
43
+ whenever the backend hiccups, which is "we could not ask" rendered as "you
44
+ may not".
45
+
3
46
  ## 0.15.1
4
47
 
5
48
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -26,6 +26,7 @@ export { WorkspaceSelectionProvider, useWorkspaceSelection, } from "./model/sele
26
26
  export type { WorkspaceSelection, WorkspaceSelectionProviderProps, WorkspaceSelectionSource, WorkspaceSelectionUrlBinding, } from "./model/selection.js";
27
27
  export { useWorkspaces, useWorkspace, useMembers, useAudit, useCanCreateWorkspace, useInvitations, useInfiniteInvitations, useRoles, useInstanceShape, useInvitationPreview, useCapabilities, useCapabilityGate, } from "./model/queries.js";
28
28
  export type { CapabilitiesResult, CapabilityGate } from "./model/queries.js";
29
+ export { useMandateState } from "./model/mandate.js";
29
30
  export { capabilityMatches, hasCapability } from "./model/capabilities.js";
30
31
  export { BUILTIN_CAPABILITY_LEVELS, SENSITIVE_SCOPE, capabilityLevel, readVerificationEnrollment, } from "./model/stepUp.js";
31
32
  export type { CapabilityLevel, VerificationEnrollment, } from "./model/stepUp.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -25,6 +25,10 @@ export { workspacesQueryKeys } from "./model/queryKeys.js";
25
25
  export { WorkspaceSelectionProvider, useWorkspaceSelection, } from "./model/selection.js";
26
26
  // ── model (read hooks) ───────────────────────────────────────────────────────
27
27
  export { useWorkspaces, useWorkspace, useMembers, useAudit, useCanCreateWorkspace, useInvitations, useInfiniteInvitations, useRoles, useInstanceShape, useInvitationPreview, useCapabilities, useCapabilityGate, } from "./model/queries.js";
28
+ // ── model (the mandate axis: anonymous / guest / member / unresolved) ────────
29
+ // The one reader of the wire's `is_guest`. `unresolved` is a wait or an
30
+ // explained error, never a hide — render it with core's `matchMandate`.
31
+ export { useMandateState } from "./model/mandate.js";
28
32
  // ── model (capability matcher + email-mask ports — backend-synced utils) ─────
29
33
  export { capabilityMatches, hasCapability } from "./model/capabilities.js";
30
34
  export { BUILTIN_CAPABILITY_LEVELS, SENSITIVE_SCOPE, capabilityLevel, readVerificationEnrollment, } from "./model/stepUp.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAI7D,gFAAgF;AAChF,4EAA4E;AAC5E,4EAA4E;AAC5E,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAOvE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAMrE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOtE,gFAAgF;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAK7D,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,gFAAgF;AAChF,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAQ9B,gFAAgF;AAChF,OAAO,EACL,aAAa,EACb,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,QAAQ,EACR,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,gFAAgF;AAChF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,gFAAgF;AAChF,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AA4C9B,gFAAgF;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAExC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,gFAAgF;AAChF,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAGxB,gFAAgF;AAChF,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAI7D,gFAAgF;AAChF,4EAA4E;AAC5E,4EAA4E;AAC5E,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAOvE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAMrE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOtE,gFAAgF;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAK7D,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,gFAAgF;AAChF,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAQ9B,gFAAgF;AAChF,OAAO,EACL,aAAa,EACb,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,QAAQ,EACR,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,gFAAgF;AAChF,wEAAwE;AACxE,wEAAwE;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,gFAAgF;AAChF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,gFAAgF;AAChF,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AA4C9B,gFAAgF;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAExC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,gFAAgF;AAChF,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAGxB,gFAAgF;AAChF,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { MandateState } from "@stapel/core";
2
+ /**
3
+ * The caller's mandate: `anonymous` / `guest` / `member`, or `unresolved`
4
+ * with the reason it could not be obtained.
5
+ *
6
+ * **`unresolved` is a wait or an explained error, never a hide and never a
7
+ * refusal.** A host renders it with `matchMandate`, whose five required arms
8
+ * make that hard to get wrong. The tempting one-liner this replaces —
9
+ * `data?.is_guest ?? true` — turns every backend hiccup into "you are a
10
+ * guest", which locks members out of their own product and tells them
11
+ * nothing; there is no expression of that shape available here, because the
12
+ * failed and pending states carry no principal to read.
13
+ *
14
+ * The list read is the same `useWorkspaces()` a screen already runs, so the
15
+ * axis costs no extra request: TanStack serves both from one query.
16
+ */
17
+ export declare function useMandateState(): MandateState;
18
+ //# sourceMappingURL=mandate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mandate.d.ts","sourceRoot":"","sources":["../../src/model/mandate.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAWjD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,IAAI,YAAY,CA0B9C"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The mandate axis, wired to this module's wire — the single point of truth
3
+ * for "does this person hold a mandate anywhere".
4
+ *
5
+ * The vocabulary and the "unresolved is not a verdict" discipline live in
6
+ * `@stapel/core` (`mandate.ts`); this file is the only thing that knows
7
+ * WHERE the answer comes from, and it comes from two sources that already
8
+ * exist and needed no new endpoint:
9
+ *
10
+ * - the ACTIVE session's status (`@stapel/core`), which settles anonymous
11
+ * and no-session without asking anyone;
12
+ * - `is_guest` on the workspace list, which stapel-workspaces has sent
13
+ * since 0.19 — the wire form of the backend's own `permissions.is_guest`
14
+ * predicate — and which nothing on the frontend had ever read. That is
15
+ * why a registered, mandate-less person was handed every module's nav
16
+ * entry and mounted every screen, each of which then answered 403.
17
+ *
18
+ * The server evaluates the predicate; this does not re-derive it. A caller
19
+ * can hold membership ROWS that grant no mandate (every one suspended), so
20
+ * `workspaces.length` is not the question and is consulted only when talking
21
+ * to a backend too old to answer it.
22
+ */
23
+ import { useMemo } from "react";
24
+ import { loadStateFromQuery, mandateAsking, mandateResolved, mandateUnavailable, useActiveSessionStatus, } from "@stapel/core";
25
+ import { useWorkspaces } from "./queries.js";
26
+ /** Guest per the server, falling back to "no memberships" only against a
27
+ * backend that predates `is_guest` (optional on the wire). Reached only with
28
+ * a READY response in hand — an absent list is never an empty one. */
29
+ function listSaysGuest(list) {
30
+ return list.is_guest ?? (list.workspaces ?? []).length === 0;
31
+ }
32
+ /**
33
+ * The caller's mandate: `anonymous` / `guest` / `member`, or `unresolved`
34
+ * with the reason it could not be obtained.
35
+ *
36
+ * **`unresolved` is a wait or an explained error, never a hide and never a
37
+ * refusal.** A host renders it with `matchMandate`, whose five required arms
38
+ * make that hard to get wrong. The tempting one-liner this replaces —
39
+ * `data?.is_guest ?? true` — turns every backend hiccup into "you are a
40
+ * guest", which locks members out of their own product and tells them
41
+ * nothing; there is no expression of that shape available here, because the
42
+ * failed and pending states carry no principal to read.
43
+ *
44
+ * The list read is the same `useWorkspaces()` a screen already runs, so the
45
+ * axis costs no extra request: TanStack serves both from one query.
46
+ */
47
+ export function useMandateState() {
48
+ const sessionStatus = useActiveSessionStatus();
49
+ const query = useWorkspaces();
50
+ const list = loadStateFromQuery(query);
51
+ return useMemo(() => {
52
+ // "We have not checked yet" — the session's own fourth state, and the
53
+ // same reason `useWorkspaces` is gated: answering here would be a guess.
54
+ if (sessionStatus === "initializing")
55
+ return mandateAsking();
56
+ // Neither carries an identity that could hold a membership, and asking
57
+ // the server would only spend a request to be told so.
58
+ if (sessionStatus === "anonymous" || sessionStatus === "unauthenticated") {
59
+ return mandateResolved("anonymous");
60
+ }
61
+ // Authenticated — or no session-owning module registered at all, in
62
+ // which case the server's answer about its own caller is the only
63
+ // evidence there is, and it is a better one than a local guess.
64
+ switch (list.status) {
65
+ case "loading":
66
+ return mandateAsking();
67
+ case "failed":
68
+ return mandateUnavailable(list.error);
69
+ case "ready":
70
+ return mandateResolved(listSaysGuest(list.data) ? "guest" : "member");
71
+ }
72
+ }, [sessionStatus, list]);
73
+ }
74
+ //# sourceMappingURL=mandate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mandate.js","sourceRoot":"","sources":["../../src/model/mandate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C;;sEAEsE;AACtE,SAAS,aAAa,CAAC,IAAmB;IACxC,OAAO,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,aAAa,GAAG,sBAAsB,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAEvC,OAAO,OAAO,CAAC,GAAiB,EAAE;QAChC,sEAAsE;QACtE,yEAAyE;QACzE,IAAI,aAAa,KAAK,cAAc;YAAE,OAAO,aAAa,EAAE,CAAC;QAC7D,uEAAuE;QACvE,uDAAuD;QACvD,IAAI,aAAa,KAAK,WAAW,IAAI,aAAa,KAAK,iBAAiB,EAAE,CAAC;YACzE,OAAO,eAAe,CAAC,WAAW,CAAC,CAAC;QACtC,CAAC;QACD,oEAAoE;QACpE,kEAAkE;QAClE,gEAAgE;QAChE,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,KAAK,SAAS;gBACZ,OAAO,aAAa,EAAE,CAAC;YACzB,KAAK,QAAQ;gBACX,OAAO,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxC,KAAK,OAAO;gBACV,OAAO,eAAe,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;AAC5B,CAAC"}
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @stapel/workspaces-react 0.15.1
1
+ # @stapel/workspaces-react 0.16.0
2
2
 
3
3
  Headless React flow pair for stapel-workspaces (contract >=0.24 <0.25) — business + state, zero visual opinion.
4
4
  Built on @stapel/core: typed client + StapelApiError envelope, auth token refresh,
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$generated": "by scripts/gen-manifest.mjs — do not edit; drift-gated (pnpm gen:manifest:check)",
3
3
  "package": "@stapel/workspaces-react",
4
- "version": "0.15.1",
4
+ "version": "0.16.0",
5
5
  "backend": {
6
6
  "module": "stapel-workspaces",
7
7
  "contract": ">=0.24 <0.25"
@@ -1178,6 +1178,7 @@
1178
1178
  "useInvitationPreview",
1179
1179
  "useInvitations",
1180
1180
  "useInviteMembers",
1181
+ "useMandateState",
1181
1182
  "useMembers",
1182
1183
  "useRemoveMember",
1183
1184
  "useRenameInvitation",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stapel/workspaces-react",
3
- "version": "0.15.1",
3
+ "version": "0.16.0",
4
4
  "description": "Headless React flow pair for stapel-workspaces: typed API client, TanStack Query hooks, flow machines, headless components, and i18n keys. Zero visual opinion.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -67,7 +67,7 @@
67
67
  }
68
68
  ],
69
69
  "peerDependencies": {
70
- "@stapel/core": ">=0.13.0 <1.0.0",
70
+ "@stapel/core": ">=0.14.0 <1.0.0",
71
71
  "@tanstack/react-query": "^5.0.0",
72
72
  "antd": ">=5.20.0 <7",
73
73
  "react": ">=19"
@@ -91,7 +91,7 @@
91
91
  "size-limit": "^11.2.0",
92
92
  "typescript": "^5.8.3",
93
93
  "vitest": "^3.2.4",
94
- "@stapel/core": "^0.13.0",
94
+ "@stapel/core": "^0.14.0",
95
95
  "@stapel/showcase": "^0.2.0",
96
96
  "@stapel/tokens": "^0.5.0"
97
97
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$generated": "by scripts/gen-events.mjs — do not edit; drift-gated (pnpm gen:events:check)",
3
3
  "package": "@stapel/workspaces-react",
4
- "version": "0.15.1",
4
+ "version": "0.16.0",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }
package/src/index.ts CHANGED
@@ -81,6 +81,11 @@ export {
81
81
  } from "./model/queries.js";
82
82
  export type { CapabilitiesResult, CapabilityGate } from "./model/queries.js";
83
83
 
84
+ // ── model (the mandate axis: anonymous / guest / member / unresolved) ────────
85
+ // The one reader of the wire's `is_guest`. `unresolved` is a wait or an
86
+ // explained error, never a hide — render it with core's `matchMandate`.
87
+ export { useMandateState } from "./model/mandate.js";
88
+
84
89
  // ── model (capability matcher + email-mask ports — backend-synced utils) ─────
85
90
  export { capabilityMatches, hasCapability } from "./model/capabilities.js";
86
91
  export {
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The mandate axis, wired to this module's wire — the single point of truth
3
+ * for "does this person hold a mandate anywhere".
4
+ *
5
+ * The vocabulary and the "unresolved is not a verdict" discipline live in
6
+ * `@stapel/core` (`mandate.ts`); this file is the only thing that knows
7
+ * WHERE the answer comes from, and it comes from two sources that already
8
+ * exist and needed no new endpoint:
9
+ *
10
+ * - the ACTIVE session's status (`@stapel/core`), which settles anonymous
11
+ * and no-session without asking anyone;
12
+ * - `is_guest` on the workspace list, which stapel-workspaces has sent
13
+ * since 0.19 — the wire form of the backend's own `permissions.is_guest`
14
+ * predicate — and which nothing on the frontend had ever read. That is
15
+ * why a registered, mandate-less person was handed every module's nav
16
+ * entry and mounted every screen, each of which then answered 403.
17
+ *
18
+ * The server evaluates the predicate; this does not re-derive it. A caller
19
+ * can hold membership ROWS that grant no mandate (every one suspended), so
20
+ * `workspaces.length` is not the question and is consulted only when talking
21
+ * to a backend too old to answer it.
22
+ */
23
+ import { useMemo } from "react";
24
+ import {
25
+ loadStateFromQuery,
26
+ mandateAsking,
27
+ mandateResolved,
28
+ mandateUnavailable,
29
+ useActiveSessionStatus,
30
+ } from "@stapel/core";
31
+ import type { MandateState } from "@stapel/core";
32
+ import type { WorkspaceList } from "../api/types.js";
33
+ import { useWorkspaces } from "./queries.js";
34
+
35
+ /** Guest per the server, falling back to "no memberships" only against a
36
+ * backend that predates `is_guest` (optional on the wire). Reached only with
37
+ * a READY response in hand — an absent list is never an empty one. */
38
+ function listSaysGuest(list: WorkspaceList): boolean {
39
+ return list.is_guest ?? (list.workspaces ?? []).length === 0;
40
+ }
41
+
42
+ /**
43
+ * The caller's mandate: `anonymous` / `guest` / `member`, or `unresolved`
44
+ * with the reason it could not be obtained.
45
+ *
46
+ * **`unresolved` is a wait or an explained error, never a hide and never a
47
+ * refusal.** A host renders it with `matchMandate`, whose five required arms
48
+ * make that hard to get wrong. The tempting one-liner this replaces —
49
+ * `data?.is_guest ?? true` — turns every backend hiccup into "you are a
50
+ * guest", which locks members out of their own product and tells them
51
+ * nothing; there is no expression of that shape available here, because the
52
+ * failed and pending states carry no principal to read.
53
+ *
54
+ * The list read is the same `useWorkspaces()` a screen already runs, so the
55
+ * axis costs no extra request: TanStack serves both from one query.
56
+ */
57
+ export function useMandateState(): MandateState {
58
+ const sessionStatus = useActiveSessionStatus();
59
+ const query = useWorkspaces();
60
+ const list = loadStateFromQuery(query);
61
+
62
+ return useMemo((): MandateState => {
63
+ // "We have not checked yet" — the session's own fourth state, and the
64
+ // same reason `useWorkspaces` is gated: answering here would be a guess.
65
+ if (sessionStatus === "initializing") return mandateAsking();
66
+ // Neither carries an identity that could hold a membership, and asking
67
+ // the server would only spend a request to be told so.
68
+ if (sessionStatus === "anonymous" || sessionStatus === "unauthenticated") {
69
+ return mandateResolved("anonymous");
70
+ }
71
+ // Authenticated — or no session-owning module registered at all, in
72
+ // which case the server's answer about its own caller is the only
73
+ // evidence there is, and it is a better one than a local guess.
74
+ switch (list.status) {
75
+ case "loading":
76
+ return mandateAsking();
77
+ case "failed":
78
+ return mandateUnavailable(list.error);
79
+ case "ready":
80
+ return mandateResolved(listSaysGuest(list.data) ? "guest" : "member");
81
+ }
82
+ }, [sessionStatus, list]);
83
+ }