@stapel/workspaces-react 0.15.1 → 0.17.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,59 @@
1
1
  # @stapel/workspaces-react
2
2
 
3
+ ## 0.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ace82db: `useMandateSource()` — this module's derivation in the shape core's
8
+ `<MandateProvider>` takes. A screen reads the axis with `useMandate()` from
9
+ `@stapel/core` and no longer imports this package to do it, which is what
10
+ lets the same screen work on a public surface that has no workspace list at
11
+ all. `useMandateState()` and its behaviour are unchanged; the `@stapel/core`
12
+ floor rises to the release that carries the seam.
13
+
14
+ ## 0.16.0
15
+
16
+ ### Minor Changes
17
+
18
+ - e25e9a6: `useMandateState()`, and a `resolveNav` that consumes the surface axis.
19
+
20
+ `is_guest` has ridden the workspace-list response since stapel-workspaces
21
+ 0.19 and had **zero readers**. `useMandateState()` is the first one: the
22
+ single point of truth for "does this person hold a mandate anywhere",
23
+ computed from two answers that already existed — the active session's status
24
+ (which settles anonymous and no-session without asking anyone) and the
25
+ server's own `is_guest` predicate. No new endpoint, and no extra request:
26
+ it reads the same `useWorkspaces()` a screen is already running.
27
+
28
+ The server evaluates the predicate; the hook does not re-derive it. A caller
29
+ can hold membership rows that grant no mandate, so `workspaces.length` is not
30
+ the question — it is consulted only against a backend too old to answer.
31
+
32
+ The unresolved case is the reason the hook is shaped the way it is. A list in
33
+ flight and a list that 502'd both resolve to `unresolved` with a reason, and
34
+ neither ever resolves to `guest`. The one-liner this forecloses —
35
+ `data?.is_guest ?? true` — turns every backend hiccup into "you are a guest",
36
+ locks members out of their own product, and explains nothing; there is no
37
+ expression of that shape available, because the pending and failed states
38
+ carry no principal to read. Render it with `matchMandate`: a wait, or the
39
+ outage stated out loud.
40
+
41
+ `resolveNav` now takes an optional `{ audience }` and every `ResolvedNavEntry`
42
+ carries its resolved `surface`. Omit the audience and nothing changes — the
43
+ scaffold-codegen call site keeps baking every route, and so does every
44
+ existing runtime caller. Pass one and a screen closed to that principal is
45
+ dropped, menu entry and route together, which is the fix: the tree a host
46
+ mounts from is the tree the axis filtered. A project's override file can flip
47
+ `menuVisible` and `order`; it deliberately cannot flip this, because a
48
+ per-project preference must not put a screen that will refuse the caller back
49
+ in front of them.
50
+
51
+ `audience` is a `MandatePrincipal`, so `"unresolved"` cannot be passed. A host
52
+ whose mandate has not settled has to render the wait or the error rather than
53
+ resolve a nav for it — the alternative is a menu that quietly empties itself
54
+ whenever the backend hiccups, which is "we could not ask" rendered as "you
55
+ may not".
56
+
3
57
  ## 0.15.1
4
58
 
5
59
  ### 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, useMandateSource } 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,14 @@ 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
+ // `useMandateSource` is the same derivation in the shape core's
32
+ // `<MandateProvider>` takes: a surface reads the axis through `useMandate()`
33
+ // without importing this package, which is what lets a public storefront
34
+ // have a mandate at all.
35
+ export { useMandateState, useMandateSource } from "./model/mandate.js";
28
36
  // ── model (capability matcher + email-mask ports — backend-synced utils) ─────
29
37
  export { capabilityMatches, hasCapability } from "./model/capabilities.js";
30
38
  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,gEAAgE;AAChE,6EAA6E;AAC7E,yEAAyE;AACzE,yBAAyB;AACzB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEvE,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,40 @@
1
+ import type { MandateSource, 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
+ /**
19
+ * This module's derivation, as core's {@link MandateSource} — what a tenant
20
+ * app hands `<MandateProvider>`.
21
+ *
22
+ * The seam exists so that reading the axis and deriving it stop being the
23
+ * same dependency: a public storefront has no workspace list to ask and must
24
+ * not import this package to render a header, while an app that DOES have
25
+ * one wires it here, once, at the root. Nothing about the derivation changes
26
+ * — this is `useMandateState()` in the shape the provider takes.
27
+ *
28
+ * The wrapper is deliberately not memoised: `useMandateState` recomputes from
29
+ * a query result that is a new object on every render, so a memo here would
30
+ * buy nothing. `MandateProvider` compares the answer itself before
31
+ * republishing, which is the guarantee that actually holds.
32
+ *
33
+ * ```tsx
34
+ * function Root({ children }: { children: ReactNode }) {
35
+ * return <MandateProvider source={useMandateSource()}>{children}</MandateProvider>;
36
+ * }
37
+ * ```
38
+ */
39
+ export declare function useMandateSource(): MandateSource;
40
+ //# 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,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAWhE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,IAAI,YAAY,CA0B9C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD"}
@@ -0,0 +1,98 @@
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
+ /**
75
+ * This module's derivation, as core's {@link MandateSource} — what a tenant
76
+ * app hands `<MandateProvider>`.
77
+ *
78
+ * The seam exists so that reading the axis and deriving it stop being the
79
+ * same dependency: a public storefront has no workspace list to ask and must
80
+ * not import this package to render a header, while an app that DOES have
81
+ * one wires it here, once, at the root. Nothing about the derivation changes
82
+ * — this is `useMandateState()` in the shape the provider takes.
83
+ *
84
+ * The wrapper is deliberately not memoised: `useMandateState` recomputes from
85
+ * a query result that is a new object on every render, so a memo here would
86
+ * buy nothing. `MandateProvider` compares the answer itself before
87
+ * republishing, which is the guarantee that actually holds.
88
+ *
89
+ * ```tsx
90
+ * function Root({ children }: { children: ReactNode }) {
91
+ * return <MandateProvider source={useMandateSource()}>{children}</MandateProvider>;
92
+ * }
93
+ * ```
94
+ */
95
+ export function useMandateSource() {
96
+ return { state: useMandateState() };
97
+ }
98
+ //# 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;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,CAAC;AACtC,CAAC"}
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @stapel/workspaces-react 0.15.1
1
+ # @stapel/workspaces-react 0.17.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.17.0",
5
5
  "backend": {
6
6
  "module": "stapel-workspaces",
7
7
  "contract": ">=0.24 <0.25"
@@ -1178,6 +1178,8 @@
1178
1178
  "useInvitationPreview",
1179
1179
  "useInvitations",
1180
1180
  "useInviteMembers",
1181
+ "useMandateSource",
1182
+ "useMandateState",
1181
1183
  "useMembers",
1182
1184
  "useRemoveMember",
1183
1185
  "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.17.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.15.0 <1.0.0",
71
71
  "@tanstack/react-query": "^5.0.0",
72
72
  "antd": ">=5.20.0 <7",
73
73
  "react": ">=19"
@@ -91,9 +91,9 @@
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",
95
- "@stapel/showcase": "^0.2.0",
96
- "@stapel/tokens": "^0.5.0"
94
+ "@stapel/core": "^0.15.0",
95
+ "@stapel/tokens": "^0.5.0",
96
+ "@stapel/showcase": "^0.2.0"
97
97
  },
98
98
  "engines": {
99
99
  "node": ">=22"
@@ -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.17.0",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }
package/src/index.ts CHANGED
@@ -81,6 +81,15 @@ 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
+ // `useMandateSource` is the same derivation in the shape core's
88
+ // `<MandateProvider>` takes: a surface reads the axis through `useMandate()`
89
+ // without importing this package, which is what lets a public storefront
90
+ // have a mandate at all.
91
+ export { useMandateState, useMandateSource } from "./model/mandate.js";
92
+
84
93
  // ── model (capability matcher + email-mask ports — backend-synced utils) ─────
85
94
  export { capabilityMatches, hasCapability } from "./model/capabilities.js";
86
95
  export {
@@ -0,0 +1,108 @@
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 { MandateSource, 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
+ }
84
+
85
+ /**
86
+ * This module's derivation, as core's {@link MandateSource} — what a tenant
87
+ * app hands `<MandateProvider>`.
88
+ *
89
+ * The seam exists so that reading the axis and deriving it stop being the
90
+ * same dependency: a public storefront has no workspace list to ask and must
91
+ * not import this package to render a header, while an app that DOES have
92
+ * one wires it here, once, at the root. Nothing about the derivation changes
93
+ * — this is `useMandateState()` in the shape the provider takes.
94
+ *
95
+ * The wrapper is deliberately not memoised: `useMandateState` recomputes from
96
+ * a query result that is a new object on every render, so a memo here would
97
+ * buy nothing. `MandateProvider` compares the answer itself before
98
+ * republishing, which is the guarantee that actually holds.
99
+ *
100
+ * ```tsx
101
+ * function Root({ children }: { children: ReactNode }) {
102
+ * return <MandateProvider source={useMandateSource()}>{children}</MandateProvider>;
103
+ * }
104
+ * ```
105
+ */
106
+ export function useMandateSource(): MandateSource {
107
+ return { state: useMandateState() };
108
+ }