@stapel/workspaces-react 0.5.0 → 0.6.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,31 @@
1
1
  # @stapel/workspaces-react
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6ef6c44: Gate top-level "the caller's own …" query hooks on `@stapel/core`'s new
8
+ `useActiveSessionReady()` (owner-diagnosed live incident, 2026-07-17): a hook
9
+ with no natural `enabled` condition of its own (`useWorkspaces`, `useWallet`,
10
+ `useTransactions`, `useSubscription`, `useNotificationFeed`/
11
+ `useInfiniteNotificationFeed`, `useCalendar`/`useEvents`/`useAvailability`,
12
+ `useRecordings`) fires the instant a component mounts — which used to race a
13
+ session still bootstrapping and read a live one as "expired". Detail hooks
14
+ keyed by an id (`useWorkspace`/`useMembers`/`useEvent`/`useRecording`) now
15
+ ALSO gate on session readiness in addition to their existing non-empty-id
16
+ check, since an id can be known synchronously (e.g. a URL param) before the
17
+ session settles.
18
+
19
+ Deliberately NOT gated: `useCatalog` (billing) and `useLanguages` (profiles,
20
+ unaffected by this changeset but worth noting for symmetry) — both are
21
+ public reference lists a signed-out visitor legitimately needs.
22
+
23
+ Zero manual wiring at any call site: `useActiveSessionReady()` reads
24
+ whichever `SessionManager` a session-owning module (e.g.
25
+ `@stapel/auth-react`'s `createAuthRuntime`) registered as "active", and
26
+ defaults to `true` (never blocks) when no such module exists in the host at
27
+ all.
28
+
3
29
  ## 0.5.0
4
30
 
5
31
  ### Minor Changes
@@ -6,18 +6,32 @@ import type { MemberPage, MembersParams, Workspace, WorkspaceList } from "../api
6
6
  * override per call site via a page that needs fresher data. Keys are
7
7
  * namespaced (see `workspacesQueryKeys`).
8
8
  */
9
- /** The caller's workspaces — their accepted memberships (GET /). */
9
+ /**
10
+ * The caller's workspaces — their accepted memberships (GET /).
11
+ *
12
+ * Gated on {@link useActiveSessionReady} (owner-diagnosed live incident,
13
+ * 2026-07-17): unlike {@link useWorkspace}/{@link useMembers} below (both
14
+ * naturally disabled until a `workspaceId` is picked), this is the top-level
15
+ * list hook with nothing else to gate it — it fires the instant a component
16
+ * mounts. Without the session ready-gate this is exactly the shape of hook
17
+ * that raced a session still bootstrapping (e.g. right after a QR
18
+ * `session_share` scan set fresh cookies this JS runtime hadn't caught up
19
+ * to yet) and read a live session as "expired" before it had a chance to
20
+ * resolve — zero manual `enabled` wiring needed at each call site by design.
21
+ */
10
22
  export declare function useWorkspaces(): UseQueryResult<WorkspaceList, StapelApiError>;
11
23
  /**
12
24
  * A single workspace by id (GET /{id}). Disabled until a `workspaceId` is
13
- * given, so a detail hook can mount before a selection exists.
25
+ * given, so a detail hook can mount before a selection exists — AND until
26
+ * the session is ready (a `workspaceId` can be known synchronously, e.g.
27
+ * from a URL param, before the session has finished bootstrapping).
14
28
  */
15
29
  export declare function useWorkspace(workspaceId: string | null): UseQueryResult<Workspace, StapelApiError>;
16
30
  /**
17
31
  * A page of a workspace's members (GET /{id}/members, anchor-paginated).
18
- * Disabled until a `workspaceId` is given. Pass `{ anchor, direction, limit,
19
- * search }` to jump to a specific page or filter; omit for the newest page
20
- * (default limit 100).
32
+ * Disabled until a `workspaceId` is given (and the session is ready — see
33
+ * {@link useWorkspace}). Pass `{ anchor, direction, limit, search }` to jump
34
+ * to a specific page or filter; omit for the newest page (default limit 100).
21
35
  */
22
36
  export declare function useMembers(workspaceId: string | null, params?: MembersParams): UseQueryResult<MemberPage, StapelApiError>;
23
37
  //# sourceMappingURL=queries.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/model/queries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,SAAS,EACT,aAAa,EACd,MAAM,iBAAiB,CAAC;AAIzB;;;;GAIG;AAEH,oEAAoE;AACpE,wBAAgB,aAAa,IAAI,cAAc,CAAC,aAAa,EAAE,cAAc,CAAC,CAM7E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,CAO3C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,MAAM,CAAC,EAAE,aAAa,GACrB,cAAc,CAAC,UAAU,EAAE,cAAc,CAAC,CAQ5C"}
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/model/queries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,SAAS,EACT,aAAa,EACd,MAAM,iBAAiB,CAAC;AAIzB;;;;GAIG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,IAAI,cAAc,CAAC,aAAa,EAAE,cAAc,CAAC,CAQ7E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,CAQ3C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,MAAM,CAAC,EAAE,aAAa,GACrB,cAAc,CAAC,UAAU,EAAE,cAAc,CAAC,CAS5C"}
@@ -1,4 +1,5 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
+ import { useActiveSessionReady } from "@stapel/core";
2
3
  import { useWorkspacesApi } from "./context.js";
3
4
  import { workspacesQueryKeys } from "./queryKeys.js";
4
5
  /**
@@ -6,39 +7,57 @@ import { workspacesQueryKeys } from "./queryKeys.js";
6
7
  * override per call site via a page that needs fresher data. Keys are
7
8
  * namespaced (see `workspacesQueryKeys`).
8
9
  */
9
- /** The caller's workspaces — their accepted memberships (GET /). */
10
+ /**
11
+ * The caller's workspaces — their accepted memberships (GET /).
12
+ *
13
+ * Gated on {@link useActiveSessionReady} (owner-diagnosed live incident,
14
+ * 2026-07-17): unlike {@link useWorkspace}/{@link useMembers} below (both
15
+ * naturally disabled until a `workspaceId` is picked), this is the top-level
16
+ * list hook with nothing else to gate it — it fires the instant a component
17
+ * mounts. Without the session ready-gate this is exactly the shape of hook
18
+ * that raced a session still bootstrapping (e.g. right after a QR
19
+ * `session_share` scan set fresh cookies this JS runtime hadn't caught up
20
+ * to yet) and read a live session as "expired" before it had a chance to
21
+ * resolve — zero manual `enabled` wiring needed at each call site by design.
22
+ */
10
23
  export function useWorkspaces() {
11
24
  const api = useWorkspacesApi();
25
+ const sessionReady = useActiveSessionReady();
12
26
  return useQuery({
13
27
  queryKey: workspacesQueryKeys.list(),
14
28
  queryFn: () => api.listWorkspaces(),
29
+ enabled: sessionReady,
15
30
  });
16
31
  }
17
32
  /**
18
33
  * A single workspace by id (GET /{id}). Disabled until a `workspaceId` is
19
- * given, so a detail hook can mount before a selection exists.
34
+ * given, so a detail hook can mount before a selection exists — AND until
35
+ * the session is ready (a `workspaceId` can be known synchronously, e.g.
36
+ * from a URL param, before the session has finished bootstrapping).
20
37
  */
21
38
  export function useWorkspace(workspaceId) {
22
39
  const api = useWorkspacesApi();
40
+ const sessionReady = useActiveSessionReady();
23
41
  return useQuery({
24
42
  queryKey: workspacesQueryKeys.detail(workspaceId ?? ""),
25
43
  queryFn: () => api.getWorkspace(workspaceId),
26
- enabled: workspaceId !== null && workspaceId !== "",
44
+ enabled: sessionReady && workspaceId !== null && workspaceId !== "",
27
45
  });
28
46
  }
29
47
  /**
30
48
  * A page of a workspace's members (GET /{id}/members, anchor-paginated).
31
- * Disabled until a `workspaceId` is given. Pass `{ anchor, direction, limit,
32
- * search }` to jump to a specific page or filter; omit for the newest page
33
- * (default limit 100).
49
+ * Disabled until a `workspaceId` is given (and the session is ready — see
50
+ * {@link useWorkspace}). Pass `{ anchor, direction, limit, search }` to jump
51
+ * to a specific page or filter; omit for the newest page (default limit 100).
34
52
  */
35
53
  export function useMembers(workspaceId, params) {
36
54
  const api = useWorkspacesApi();
55
+ const sessionReady = useActiveSessionReady();
37
56
  const p = params ?? {};
38
57
  return useQuery({
39
58
  queryKey: workspacesQueryKeys.membersPage(workspaceId ?? "", p),
40
59
  queryFn: () => api.listMembers(workspaceId, p),
41
- enabled: workspaceId !== null && workspaceId !== "",
60
+ enabled: sessionReady && workspaceId !== null && workspaceId !== "",
42
61
  });
43
62
  }
44
63
  //# sourceMappingURL=queries.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/model/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AASjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;GAIG;AAEH,oEAAoE;AACpE,MAAM,UAAU,aAAa;IAC3B,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,mBAAmB,CAAC,IAAI,EAAE;QACpC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,cAAc,EAAE;KACpC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,WAA0B;IAE1B,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,mBAAmB,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;QACvD,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,WAAqB,CAAC;QACtD,OAAO,EAAE,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,EAAE;KACpD,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,WAA0B,EAC1B,MAAsB;IAEtB,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,MAAM,CAAC,GAAG,MAAM,IAAI,EAAE,CAAC;IACvB,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,mBAAmB,CAAC,WAAW,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/D,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,WAAqB,EAAE,CAAC,CAAC;QACxD,OAAO,EAAE,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,EAAE;KACpD,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/model/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAQrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;GAIG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,qBAAqB,EAAE,CAAC;IAC7C,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,mBAAmB,CAAC,IAAI,EAAE;QACpC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,cAAc,EAAE;QACnC,OAAO,EAAE,YAAY;KACtB,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,WAA0B;IAE1B,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,qBAAqB,EAAE,CAAC;IAC7C,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,mBAAmB,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;QACvD,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,WAAqB,CAAC;QACtD,OAAO,EAAE,YAAY,IAAI,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,EAAE;KACpE,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,WAA0B,EAC1B,MAAsB;IAEtB,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,qBAAqB,EAAE,CAAC;IAC7C,MAAM,CAAC,GAAG,MAAM,IAAI,EAAE,CAAC;IACvB,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,mBAAmB,CAAC,WAAW,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/D,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,WAAqB,EAAE,CAAC,CAAC;QACxD,OAAO,EAAE,YAAY,IAAI,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,EAAE;KACpE,CAAC,CAAC;AACL,CAAC"}
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @stapel/workspaces-react 0.5.0
1
+ # @stapel/workspaces-react 0.6.0
2
2
 
3
3
  Headless React flow pair for stapel-workspaces (contract >=0.4 <0.5) — 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.5.0",
4
+ "version": "0.6.0",
5
5
  "backend": {
6
6
  "module": "stapel-workspaces",
7
7
  "contract": ">=0.4 <0.5"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stapel/workspaces-react",
3
- "version": "0.5.0",
3
+ "version": "0.6.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": {
@@ -82,9 +82,9 @@
82
82
  "size-limit": "^11.2.0",
83
83
  "typescript": "^5.8.3",
84
84
  "vitest": "^3.2.4",
85
- "@stapel/core": "^0.5.0",
86
- "@stapel/showcase": "^0.2.0",
87
- "@stapel/tokens": "^0.4.0"
85
+ "@stapel/core": "^0.6.0",
86
+ "@stapel/tokens": "^0.4.0",
87
+ "@stapel/showcase": "^0.2.0"
88
88
  },
89
89
  "engines": {
90
90
  "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.5.0",
4
+ "version": "0.6.0",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }
@@ -1,5 +1,6 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
2
  import type { UseQueryResult } from "@tanstack/react-query";
3
+ import { useActiveSessionReady } from "@stapel/core";
3
4
  import type { StapelApiError } from "@stapel/core";
4
5
  import type {
5
6
  MemberPage,
@@ -16,45 +17,63 @@ import { workspacesQueryKeys } from "./queryKeys.js";
16
17
  * namespaced (see `workspacesQueryKeys`).
17
18
  */
18
19
 
19
- /** The caller's workspaces — their accepted memberships (GET /). */
20
+ /**
21
+ * The caller's workspaces — their accepted memberships (GET /).
22
+ *
23
+ * Gated on {@link useActiveSessionReady} (owner-diagnosed live incident,
24
+ * 2026-07-17): unlike {@link useWorkspace}/{@link useMembers} below (both
25
+ * naturally disabled until a `workspaceId` is picked), this is the top-level
26
+ * list hook with nothing else to gate it — it fires the instant a component
27
+ * mounts. Without the session ready-gate this is exactly the shape of hook
28
+ * that raced a session still bootstrapping (e.g. right after a QR
29
+ * `session_share` scan set fresh cookies this JS runtime hadn't caught up
30
+ * to yet) and read a live session as "expired" before it had a chance to
31
+ * resolve — zero manual `enabled` wiring needed at each call site by design.
32
+ */
20
33
  export function useWorkspaces(): UseQueryResult<WorkspaceList, StapelApiError> {
21
34
  const api = useWorkspacesApi();
35
+ const sessionReady = useActiveSessionReady();
22
36
  return useQuery({
23
37
  queryKey: workspacesQueryKeys.list(),
24
38
  queryFn: () => api.listWorkspaces(),
39
+ enabled: sessionReady,
25
40
  });
26
41
  }
27
42
 
28
43
  /**
29
44
  * A single workspace by id (GET /{id}). Disabled until a `workspaceId` is
30
- * given, so a detail hook can mount before a selection exists.
45
+ * given, so a detail hook can mount before a selection exists — AND until
46
+ * the session is ready (a `workspaceId` can be known synchronously, e.g.
47
+ * from a URL param, before the session has finished bootstrapping).
31
48
  */
32
49
  export function useWorkspace(
33
50
  workspaceId: string | null
34
51
  ): UseQueryResult<Workspace, StapelApiError> {
35
52
  const api = useWorkspacesApi();
53
+ const sessionReady = useActiveSessionReady();
36
54
  return useQuery({
37
55
  queryKey: workspacesQueryKeys.detail(workspaceId ?? ""),
38
56
  queryFn: () => api.getWorkspace(workspaceId as string),
39
- enabled: workspaceId !== null && workspaceId !== "",
57
+ enabled: sessionReady && workspaceId !== null && workspaceId !== "",
40
58
  });
41
59
  }
42
60
 
43
61
  /**
44
62
  * A page of a workspace's members (GET /{id}/members, anchor-paginated).
45
- * Disabled until a `workspaceId` is given. Pass `{ anchor, direction, limit,
46
- * search }` to jump to a specific page or filter; omit for the newest page
47
- * (default limit 100).
63
+ * Disabled until a `workspaceId` is given (and the session is ready — see
64
+ * {@link useWorkspace}). Pass `{ anchor, direction, limit, search }` to jump
65
+ * to a specific page or filter; omit for the newest page (default limit 100).
48
66
  */
49
67
  export function useMembers(
50
68
  workspaceId: string | null,
51
69
  params?: MembersParams
52
70
  ): UseQueryResult<MemberPage, StapelApiError> {
53
71
  const api = useWorkspacesApi();
72
+ const sessionReady = useActiveSessionReady();
54
73
  const p = params ?? {};
55
74
  return useQuery({
56
75
  queryKey: workspacesQueryKeys.membersPage(workspaceId ?? "", p),
57
76
  queryFn: () => api.listMembers(workspaceId as string, p),
58
- enabled: workspaceId !== null && workspaceId !== "",
77
+ enabled: sessionReady && workspaceId !== null && workspaceId !== "",
59
78
  });
60
79
  }