@stapel/workspaces-react 0.13.0 → 0.15.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/api/generated/schema.d.ts +165 -3
  3. package/dist/api/generated/schema.d.ts.map +1 -1
  4. package/dist/api/types.d.ts +22 -0
  5. package/dist/api/types.d.ts.map +1 -1
  6. package/dist/api/workspacesApi.d.ts +5 -1
  7. package/dist/api/workspacesApi.d.ts.map +1 -1
  8. package/dist/api/workspacesApi.js +16 -0
  9. package/dist/api/workspacesApi.js.map +1 -1
  10. package/dist/default/MembersManager.d.ts.map +1 -1
  11. package/dist/default/MembersManager.js +20 -6
  12. package/dist/default/MembersManager.js.map +1 -1
  13. package/dist/default/WorkspaceSettings.d.ts.map +1 -1
  14. package/dist/default/WorkspaceSettings.js +7 -3
  15. package/dist/default/WorkspaceSettings.js.map +1 -1
  16. package/dist/headless/Can.d.ts +8 -0
  17. package/dist/headless/Can.d.ts.map +1 -1
  18. package/dist/headless/Can.js +5 -1
  19. package/dist/headless/Can.js.map +1 -1
  20. package/dist/headless/Members.d.ts +14 -13
  21. package/dist/headless/Members.d.ts.map +1 -1
  22. package/dist/headless/Members.js +4 -12
  23. package/dist/headless/Members.js.map +1 -1
  24. package/dist/headless/RoleSelect.d.ts +15 -9
  25. package/dist/headless/RoleSelect.d.ts.map +1 -1
  26. package/dist/headless/RoleSelect.js +12 -8
  27. package/dist/headless/RoleSelect.js.map +1 -1
  28. package/dist/headless/WorkspaceList.d.ts +25 -10
  29. package/dist/headless/WorkspaceList.d.ts.map +1 -1
  30. package/dist/headless/WorkspaceList.js +14 -5
  31. package/dist/headless/WorkspaceList.js.map +1 -1
  32. package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -1
  33. package/dist/i18n/generated/errors.es.gen.js +2 -0
  34. package/dist/i18n/generated/errors.es.gen.js.map +1 -1
  35. package/dist/i18n/generated/errors.gen.d.ts +12 -0
  36. package/dist/i18n/generated/errors.gen.d.ts.map +1 -1
  37. package/dist/i18n/generated/errors.gen.js +6 -0
  38. package/dist/i18n/generated/errors.gen.js.map +1 -1
  39. package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -1
  40. package/dist/i18n/generated/errors.ru.gen.js +2 -0
  41. package/dist/i18n/generated/errors.ru.gen.js.map +1 -1
  42. package/dist/i18n/keys.d.ts +13 -0
  43. package/dist/i18n/keys.d.ts.map +1 -1
  44. package/dist/i18n/keys.js +23 -0
  45. package/dist/i18n/keys.js.map +1 -1
  46. package/dist/i18n/ru.d.ts.map +1 -1
  47. package/dist/i18n/ru.js +7 -0
  48. package/dist/i18n/ru.js.map +1 -1
  49. package/dist/index.d.ts +2 -2
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/model/queries.d.ts +46 -8
  54. package/dist/model/queries.d.ts.map +1 -1
  55. package/dist/model/queries.js +53 -9
  56. package/dist/model/queries.js.map +1 -1
  57. package/dist/model/queryKeys.d.ts +3 -1
  58. package/dist/model/queryKeys.d.ts.map +1 -1
  59. package/dist/model/queryKeys.js +6 -0
  60. package/dist/model/queryKeys.js.map +1 -1
  61. package/dist/model/selection.d.ts +20 -7
  62. package/dist/model/selection.d.ts.map +1 -1
  63. package/dist/model/selection.js +32 -12
  64. package/dist/model/selection.js.map +1 -1
  65. package/llms.txt +6 -3
  66. package/manifest.json +46 -3
  67. package/package.json +2 -2
  68. package/src/analytics/generated/events.json +1 -1
  69. package/src/api/generated/schema.ts +165 -3
  70. package/src/api/types.ts +23 -0
  71. package/src/api/workspacesApi.ts +22 -0
  72. package/src/default/MembersManager.tsx +69 -16
  73. package/src/default/WorkspaceSettings.tsx +41 -5
  74. package/src/headless/Can.tsx +13 -1
  75. package/src/headless/Members.tsx +18 -26
  76. package/src/headless/RoleSelect.tsx +17 -14
  77. package/src/headless/WorkspaceList.tsx +31 -14
  78. package/src/i18n/generated/errors.es.gen.ts +2 -0
  79. package/src/i18n/generated/errors.gen.ts +6 -0
  80. package/src/i18n/generated/errors.json +16 -0
  81. package/src/i18n/generated/errors.ru.gen.ts +2 -0
  82. package/src/i18n/keys.ts +26 -0
  83. package/src/i18n/ru.ts +10 -0
  84. package/src/index.ts +5 -0
  85. package/src/model/queries.ts +77 -16
  86. package/src/model/queryKeys.ts +16 -1
  87. package/src/model/selection.tsx +73 -21
@@ -13,6 +13,12 @@ export const workspacesQueryKeys = {
13
13
  // The bare 3-tuple is a valid prefix of membersPage's 4-tuple, so
14
14
  // invalidating `members(workspaceId)` (mutations.ts) drops every page.
15
15
  members: (workspaceId) => [ROOT, "members", workspaceId],
16
+ /** The workspace's membership history (stapel-workspaces 0.24). Its own
17
+ * root, not a slice of `members`: history is append-only and a member
18
+ * mutation invalidates the ROSTER — the history it also appended to is
19
+ * invalidated deliberately, by naming this key, not as a side effect. */
20
+ audit: (workspaceId) => [ROOT, "audit", workspaceId],
21
+ auditPage: (workspaceId, params) => [ROOT, "audit", workspaceId, params],
16
22
  // EVERY workspace's roster, not one. Not a read key — nothing fetches it —
17
23
  // but the invalidation scope of a write that moves a name the whole fleet
18
24
  // shares: `MemberResponse.display_name` is a live lookup in
@@ -1 +1 @@
1
- {"version":3,"file":"queryKeys.js","sourceRoot":"","sources":["../../src/model/queryKeys.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,YAAqB,CAAC;AAEnC,MAAM,CAAC,MAAM,mBAAmB,GA8B5B;IACF,GAAG,EAAE,CAAC,IAAI,CAAC;IACX,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC;IACtD,kEAAkE;IAClE,uEAAuE;IACvE,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,CAAC;IACxD,2EAA2E;IAC3E,0EAA0E;IAC1E,4DAA4D;IAC5D,wEAAwE;IACxE,sEAAsE;IACtE,mCAAmC;IACnC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC;IACnC,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC;IAC5E,0EAA0E;IAC1E,qEAAqE;IACrE,WAAW,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC;IAChE,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;QACxC,IAAI;QACJ,aAAa;QACb,WAAW;QACX,MAAM;KACP;IACD,2EAA2E;IAC3E,uEAAuE;IACvE,yDAAyD;IACzD,mBAAmB,EAAE,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QAC7C,IAAI;QACJ,aAAa;QACb,WAAW;QACX,UAAU;QACV,OAAO;KACR;IACD,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC;IAC5B,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC;IAClC,0EAA0E;IAC1E,yEAAyE;IACzE,2EAA2E;IAC3E,gBAAgB;IAChB,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,CAAC;CAClE,CAAC"}
1
+ {"version":3,"file":"queryKeys.js","sourceRoot":"","sources":["../../src/model/queryKeys.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,YAAqB,CAAC;AAEnC,MAAM,CAAC,MAAM,mBAAmB,GAmC5B;IACF,GAAG,EAAE,CAAC,IAAI,CAAC;IACX,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC;IACtD,kEAAkE;IAClE,uEAAuE;IACvE,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,CAAC;IACxD;;;6EAGyE;IACzE,KAAK,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC;IACpD,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC;IACxE,2EAA2E;IAC3E,0EAA0E;IAC1E,4DAA4D;IAC5D,wEAAwE;IACxE,sEAAsE;IACtE,mCAAmC;IACnC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC;IACnC,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC;IAC5E,0EAA0E;IAC1E,qEAAqE;IACrE,WAAW,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC;IAChE,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;QACxC,IAAI;QACJ,aAAa;QACb,WAAW;QACX,MAAM;KACP;IACD,2EAA2E;IAC3E,uEAAuE;IACvE,yDAAyD;IACzD,mBAAmB,EAAE,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QAC7C,IAAI;QACJ,aAAa;QACb,WAAW;QACX,UAAU;QACV,OAAO;KACR;IACD,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC;IAC5B,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC;IAClC,0EAA0E;IAC1E,yEAAyE;IACzE,2EAA2E;IAC3E,gBAAgB;IAChB,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,CAAC;CAClE,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { ReactElement, ReactNode } from "react";
2
- import type { Repository } from "@stapel/core";
2
+ import type { LoadState, Repository } from "@stapel/core";
3
3
  import type { Workspace } from "../api/types.js";
4
4
  /**
5
5
  * Which workspace am I in — the resolution the whole fleet was inventing
@@ -74,11 +74,26 @@ export interface WorkspaceSelectionUrlBinding {
74
74
  }
75
75
  /** What {@link useWorkspaceSelection} hands back. */
76
76
  export interface WorkspaceSelection {
77
- /** The caller's workspaces (empty before load, and for a guest). */
78
- readonly workspaces: readonly Workspace[];
79
77
  /**
80
- * The resolved workspace — `null` while loading, and for a person who
81
- * belongs to none.
78
+ * The workspace list, as a state a host cannot flatten: `loading` / `ready`
79
+ * with the rows / `failed` with the error. Render it through core's
80
+ * `matchList`.
81
+ *
82
+ * THIS FIELD IS THE FIX FOR THE 2026-08-09 INCIDENT. What used to be here
83
+ * was `workspaces: readonly Workspace[]` beside `loading: boolean`, and NO
84
+ * error field at all — so when the list endpoint answered 404 (a route
85
+ * mounted one segment too deep), a host saw `loading: false`, `workspaces:
86
+ * []`, `current: null`, and had literally no way to tell that apart from a
87
+ * person who belongs to no workspace. Every screen in the product said "you
88
+ * have no workspaces" for hours. The distinction was not merely easy to
89
+ * miss; it was ABSENT from the type.
90
+ */
91
+ readonly state: LoadState<readonly Workspace[]>;
92
+ /**
93
+ * The resolved workspace, once the list is `ready` and the chain has picked
94
+ * one. `null` while loading, when the load FAILED, and for a person who
95
+ * belongs to none — which is exactly why a screen must branch on
96
+ * {@link state} and not on this being null.
82
97
  *
83
98
  * Downstream fetching must gate on THIS, never on a raw id: an id that has
84
99
  * not been checked against the fetched list is exactly how a client ends up
@@ -94,8 +109,6 @@ export interface WorkspaceSelection {
94
109
  * this. The person has already been landed somewhere sane.
95
110
  */
96
111
  readonly urlWorkspaceInvalid: boolean;
97
- /** The workspace list is still loading. */
98
- readonly loading: boolean;
99
112
  /**
100
113
  * An EXPLICIT choice by the person — the only entry point that persists.
101
114
  * Writes the URL (push), local storage, and the backend preference.
@@ -1 +1 @@
1
- {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/model/selection.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAW,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;6EAE6E;AAC7E,MAAM,MAAM,wBAAwB,GAChC,KAAK,GACL,OAAO,GACP,YAAY,GACZ,kBAAkB,GAClB,UAAU,GACV,YAAY,CAAC;AAEjB;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC;;;;;;;;;OASG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAC9B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;QAAE,MAAM,EAAE,QAAQ,GAAG,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KACpE,IAAI,CAAC;CACX;AAED,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IACnC,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kCAAkC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAmBD,oDAAoD;AACpD,MAAM,WAAW,+BACf,SAAQ,4BAA4B;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACjD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,YAAY,CAkMd;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,kBAAkB,CAQ1D"}
1
+ {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/model/selection.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAW,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAS9D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;6EAE6E;AAC7E,MAAM,MAAM,wBAAwB,GAChC,KAAK,GACL,OAAO,GACP,YAAY,GACZ,kBAAkB,GAClB,UAAU,GACV,YAAY,CAAC;AAEjB;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC;;;;;;;;;OASG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAC9B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;QAAE,MAAM,EAAE,QAAQ,GAAG,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KACpE,IAAI,CAAC;CACX;AAED,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,SAAS,EAAE,CAAC,CAAC;IAChD;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IACnC,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kCAAkC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAuBD,oDAAoD;AACpD,MAAM,WAAW,+BACf,SAAQ,4BAA4B;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACjD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,YAAY,CA8Nd;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,kBAAkB,CAQ1D"}
@@ -1,8 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
3
- import { createRepository } from "@stapel/core";
3
+ import { bothLoaded, createRepository, loadLoading, loadReady, loadStateFromQuery, mapLoad, } from "@stapel/core";
4
4
  import { useWorkspaces } from "./queries.js";
5
5
  import { useSetPreferredWorkspace } from "./mutations.js";
6
+ /** Stable identity for the no-rows case — a fresh `[]` per render would
7
+ * re-run every consumer effect that depends on the memoised bag (#251). */
8
+ const EMPTY_WORKSPACES = [];
6
9
  const SelectionContext = createContext(null);
7
10
  /** The repository namespace and key holding the local-storage layer. */
8
11
  const REPO_NAMESPACE = "workspaces";
@@ -84,14 +87,30 @@ export function WorkspaceSelectionProvider(props) {
84
87
  cancelled = true;
85
88
  };
86
89
  }, [repo]);
87
- const workspaces = useMemo(() => query.data?.workspaces ?? [], [query.data]);
88
- const loading = query.isLoading || !localRead;
90
+ // The list read and the local-storage read must BOTH land before the chain
91
+ // can answer, and a failure of either must not read as "still loading" —
92
+ // `bothLoaded` is exactly that rule, so it is not restated here.
93
+ // Memoised on the query's OWN fields, never on the query object: TanStack
94
+ // returns a new result object every render, so `[query]` here would hand
95
+ // every consumer a new `state` on every render — the #251 loop the bag
96
+ // memoisation below exists to prevent, reintroduced one level up.
97
+ const { status: queryStatus, data: queryData, error: queryError } = query;
98
+ const state = useMemo(() => mapLoad(bothLoaded(mapLoad(loadStateFromQuery({
99
+ status: queryStatus,
100
+ data: queryData,
101
+ error: queryError,
102
+ }), (list) => list.workspaces ?? []), localRead ? loadReady(null) : loadLoading()), ([rows]) => rows), [queryStatus, queryData, queryError, localRead]);
103
+ // The resolution chain below is a COMPUTATION over rows, not a rendering of
104
+ // them, so it reads the rows flat — and it runs only when the state is
105
+ // `ready`, which is what keeps a failed load from resolving to anything.
106
+ const workspaces = state.status === "ready" ? state.data : EMPTY_WORKSPACES;
107
+ const resolvable = state.status === "ready";
89
108
  const memberOf = useCallback((id) => id ? (workspaces.find((w) => w.id === id) ?? null) : null, [workspaces]);
90
109
  // The chain. Runs against the FETCHED list only — never against an
91
110
  // unresolved one, or a raw stored id would be briefly authoritative and the
92
111
  // app would fetch a workspace it has not verified the person can open.
93
112
  const resolved = useMemo(() => {
94
- if (loading || workspaces.length === 0) {
113
+ if (!resolvable || workspaces.length === 0) {
95
114
  return { workspace: null, source: null };
96
115
  }
97
116
  const explicit = memberOf(chosenId);
@@ -115,13 +134,13 @@ export function WorkspaceSelectionProvider(props) {
115
134
  // Last resort only — see this module's header for why it is last and why
116
135
  // it is still here.
117
136
  return { workspace: workspaces[0] ?? null, source: "positional" };
118
- }, [loading, workspaces, memberOf, chosenId, urlWorkspaceId, localId, query.data]);
137
+ }, [resolvable, workspaces, memberOf, chosenId, urlWorkspaceId, localId, query.data]);
119
138
  // The URL named something the person cannot open. Deliberately NOT an
120
139
  // error state that blanks the screen, and deliberately NOT a silent
121
140
  // substitution: they clicked a link expecting workspace X, and quietly
122
141
  // showing workspace Y's data is the same misattribution class as #239.
123
142
  // They land through the rest of the chain, and this flag drives the notice.
124
- const urlWorkspaceInvalid = !loading &&
143
+ const urlWorkspaceInvalid = resolvable &&
125
144
  workspaces.length > 0 &&
126
145
  chosenId === null &&
127
146
  !!urlWorkspaceId &&
@@ -145,13 +164,16 @@ export function WorkspaceSelectionProvider(props) {
145
164
  // later-corrected backend preference or instance default; deleting it just
146
165
  // stops the next boot retrying a dead id.
147
166
  useEffect(() => {
148
- if (loading || !repo || localId === null)
167
+ // Only a SUCCESSFUL load proves a stored id is dead. Deleting it because
168
+ // the request failed would punish an outage by forgetting the person's
169
+ // workspace on every affected boot.
170
+ if (!resolvable || !repo || localId === null)
149
171
  return;
150
172
  if (memberOf(localId))
151
173
  return;
152
174
  setLocalId(null);
153
175
  void repo.del(REPO_KEY).catch(() => undefined);
154
- }, [loading, repo, localId, memberOf]);
176
+ }, [resolvable, repo, localId, memberOf]);
155
177
  /**
156
178
  * The only path that persists anything, and that is the whole write policy:
157
179
  * a URL is per-tab CONTEXT, a picker click is a CHOICE.
@@ -193,19 +215,17 @@ export function WorkspaceSelectionProvider(props) {
193
215
  // sets state, that is an unbounded render loop whose only symptom is a
194
216
  // spinner that never resolves, with nothing in the console.
195
217
  const value = useMemo(() => ({
196
- workspaces,
218
+ state,
197
219
  current: resolved.workspace,
198
220
  source: resolved.source,
199
221
  urlWorkspaceInvalid,
200
- loading,
201
222
  switchTo,
202
223
  refetch,
203
224
  }), [
204
- workspaces,
225
+ state,
205
226
  resolved.workspace,
206
227
  resolved.source,
207
228
  urlWorkspaceInvalid,
208
- loading,
209
229
  switchTo,
210
230
  refetch,
211
231
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/model/selection.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/model/selection.tsx"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
- # @stapel/workspaces-react 0.13.0
1
+ # @stapel/workspaces-react 0.15.0
2
2
 
3
- Headless React flow pair for stapel-workspaces (contract >=0.22 <0.23) — business + state, zero visual opinion.
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,
5
5
  verification-403 interception, i18n engine, analytics facade, TanStack Query layer.
6
6
 
@@ -24,6 +24,7 @@ api (typed client) · model (hooks, session) · flows (machines) · headless ·
24
24
  ## Operations (typed; use the named op, never a path string)
25
25
  Request/response schema names are in manifest.json + the generated types.
26
26
  Paths are relative to `/workspaces/api/v1/`.
27
+ - workspaces_api_v1_audit_list: GET /{workspace_id}/audit
27
28
  - workspaces_api_v1_create: POST /
28
29
  - workspaces_api_v1_destroy: DELETE /{workspace_id}
29
30
  - workspaces_api_v1_instance_retrieve: GET /instance
@@ -53,6 +54,7 @@ Paths are relative to `/workspaces/api/v1/`.
53
54
 
54
55
  ## Query hooks (server state; keys come only from the key factory)
55
56
  - useAcceptInvitation (mutation) → acceptInvitation
57
+ - useAudit (query) → listAudit
56
58
  - useClaimInvitation (mutation) → claimInvitation
57
59
  - useClearPreferredWorkspace (mutation) → clearPreferredWorkspace
58
60
  - useCreateWorkspace (mutation) → createWorkspace
@@ -78,7 +80,7 @@ Paths are relative to `/workspaces/api/v1/`.
78
80
  - useWorkspaces (query) → listWorkspaces
79
81
 
80
82
  ## Errors (render t(code, params); UX from remediation)
81
- 67 keys (full catalog: manifest.json §errors). By remediation: fix_input 33 · retry 12 · contact_support 8 · wait_and_retry 7 · verify 5 · reauthenticate 2.
83
+ 69 keys (full catalog: manifest.json §errors). By remediation: fix_input 33 · retry 12 · contact_support 9 · wait_and_retry 8 · verify 5 · reauthenticate 2.
82
84
  Param-bearing keys (interpolation slots matter):
83
85
  - error.400.field.blank [400] → fix_input {field}
84
86
  - error.400.field.does_not_exist [400] → fix_input {field}
@@ -95,6 +97,7 @@ Param-bearing keys (interpolation slots matter):
95
97
  - error.403.membership_suspended [403] → fix_input {reason}
96
98
  - error.403.missing_capability [403] → contact_support {capability}
97
99
  - error.403.role_exceeds_inviter_rank [403] → fix_input {role}
100
+ - error.429.invitation_resend_cooldown [429] → wait_and_retry {retry_after}
98
101
  - error.429.rate_limit [429] → wait_and_retry {retry_after_minutes}
99
102
 
100
103
  ## Analytics events (typed; defineEvent → events.json, drift-gated)
package/manifest.json CHANGED
@@ -1,10 +1,10 @@
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.13.0",
4
+ "version": "0.15.0",
5
5
  "backend": {
6
6
  "module": "stapel-workspaces",
7
- "contract": ">=0.22 <0.23"
7
+ "contract": ">=0.24 <0.25"
8
8
  },
9
9
  "layers": [
10
10
  "api",
@@ -18,6 +18,13 @@
18
18
  "createInviteAcceptFlow"
19
19
  ],
20
20
  "operations": {
21
+ "workspaces_api_v1_audit_list": {
22
+ "method": "GET",
23
+ "path": "/workspaces/api/v1/{workspace_id}/audit",
24
+ "tag": "Members",
25
+ "request": null,
26
+ "response": "PaginatedAuditEventResponseList"
27
+ },
21
28
  "workspaces_api_v1_create": {
22
29
  "method": "POST",
23
30
  "path": "/workspaces/api/v1/",
@@ -217,6 +224,16 @@
217
224
  ]
218
225
  ]
219
226
  },
227
+ "useAudit": {
228
+ "kind": "query",
229
+ "operation": "listAudit",
230
+ "queryKey": [
231
+ "workspaces",
232
+ "audit",
233
+ "{workspaceId}",
234
+ "{params}"
235
+ ]
236
+ },
220
237
  "useClaimInvitation": {
221
238
  "kind": "mutation",
222
239
  "operation": "claimInvitation"
@@ -725,6 +742,11 @@
725
742
  "params": [],
726
743
  "remediation": "verify"
727
744
  },
745
+ "error.403.workspace_creation_closed": {
746
+ "status": 403,
747
+ "params": [],
748
+ "remediation": "contact_support"
749
+ },
728
750
  "error.404.ad_not_found": {
729
751
  "status": 404,
730
752
  "params": [],
@@ -810,6 +832,13 @@
810
832
  "params": [],
811
833
  "remediation": "wait_and_retry"
812
834
  },
835
+ "error.429.invitation_resend_cooldown": {
836
+ "status": 429,
837
+ "params": [
838
+ "retry_after"
839
+ ],
840
+ "remediation": "wait_and_retry"
841
+ },
813
842
  "error.429.rate_limit": {
814
843
  "status": 429,
815
844
  "params": [
@@ -893,7 +922,7 @@
893
922
  {
894
923
  "id": "workspaces.list",
895
924
  "title": "Workspace list",
896
- "description": "The headless WorkspaceList wraps the read of the caller's workspaces plus an owner-seeded create, exposing workspaces / loading / creating / error state. Bring your own UI — the component is renderless.",
925
+ "description": "The headless WorkspaceList wraps the read of the caller's workspaces plus an owner-seeded create. The read arrives as a LoadState the skin renders through matchList, so 'no workspaces yet' and 'we could not load them' stay two different screens. Bring your own UI — the component is renderless.",
897
926
  "component": "WorkspaceList",
898
927
  "tokens": [
899
928
  "card-bg",
@@ -991,6 +1020,7 @@
991
1020
  "error.403.role_exceeds_inviter_rank",
992
1021
  "error.403.verification_enrollment_required",
993
1022
  "error.403.verification_required",
1023
+ "error.403.workspace_creation_closed",
994
1024
  "error.404.ad_not_found",
995
1025
  "error.404.invitation_not_found",
996
1026
  "error.404.member_not_found",
@@ -1008,6 +1038,7 @@
1008
1038
  "error.422.unprocessable_entity",
1009
1039
  "error.423.locked",
1010
1040
  "error.423.verification_locked",
1041
+ "error.429.invitation_resend_cooldown",
1011
1042
  "error.429.rate_limit",
1012
1043
  "error.429.too_many_requests",
1013
1044
  "error.500.internal",
@@ -1045,13 +1076,16 @@
1045
1076
  "workspaces.list.create",
1046
1077
  "workspaces.list.creating",
1047
1078
  "workspaces.list.empty",
1079
+ "workspaces.list.load_failed",
1048
1080
  "workspaces.list.loading",
1081
+ "workspaces.members.empty",
1049
1082
  "workspaces.members.invite",
1050
1083
  "workspaces.members.invite_dialog.emails_label",
1051
1084
  "workspaces.members.invite_dialog.emails_placeholder",
1052
1085
  "workspaces.members.invite_dialog.role_label",
1053
1086
  "workspaces.members.invite_dialog.title",
1054
1087
  "workspaces.members.inviting",
1088
+ "workspaces.members.load_failed",
1055
1089
  "workspaces.members.loading",
1056
1090
  "workspaces.members.remove",
1057
1091
  "workspaces.members.remove_confirm",
@@ -1062,10 +1096,14 @@
1062
1096
  "workspaces.members.subtitle",
1063
1097
  "workspaces.members.title",
1064
1098
  "workspaces.members.update_role",
1099
+ "workspaces.retry",
1065
1100
  "workspaces.role.admin",
1066
1101
  "workspaces.role.member",
1067
1102
  "workspaces.role.owner",
1068
1103
  "workspaces.role.viewer",
1104
+ "workspaces.roles.load_failed",
1105
+ "workspaces.settings.blocked.name_required",
1106
+ "workspaces.settings.blocked.not_owner",
1069
1107
  "workspaces.settings.danger_zone.delete",
1070
1108
  "workspaces.settings.danger_zone.delete_confirm",
1071
1109
  "workspaces.settings.danger_zone.title",
@@ -1110,6 +1148,8 @@
1110
1148
  "registerWorkspacesI18n",
1111
1149
  "toFlowError",
1112
1150
  "useAcceptInvitation",
1151
+ "useAudit",
1152
+ "useCanCreateWorkspace",
1113
1153
  "useCapabilities",
1114
1154
  "useCapabilityGate",
1115
1155
  "useClaimInvitation",
@@ -1147,6 +1187,9 @@
1147
1187
  ],
1148
1188
  "types": [
1149
1189
  "AcceptInvitationBag",
1190
+ "AuditEvent",
1191
+ "AuditPage",
1192
+ "AuditParams",
1150
1193
  "CanBag",
1151
1194
  "CapabilitiesResult",
1152
1195
  "CapabilityGate",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stapel/workspaces-react",
3
- "version": "0.13.0",
3
+ "version": "0.15.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": {
@@ -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.12.0",
94
+ "@stapel/core": "^0.13.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.13.0",
4
+ "version": "0.15.0",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }