@stapel/workspaces-react 0.1.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 (111) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/MODULE.md +54 -0
  4. package/README.md +73 -0
  5. package/dist/api/extensions.d.ts +8 -0
  6. package/dist/api/extensions.d.ts.map +1 -0
  7. package/dist/api/extensions.js +8 -0
  8. package/dist/api/extensions.js.map +1 -0
  9. package/dist/api/types.d.ts +51 -0
  10. package/dist/api/types.d.ts.map +1 -0
  11. package/dist/api/types.js +2 -0
  12. package/dist/api/types.js.map +1 -0
  13. package/dist/api/workspacesApi.d.ts +43 -0
  14. package/dist/api/workspacesApi.d.ts.map +1 -0
  15. package/dist/api/workspacesApi.js +31 -0
  16. package/dist/api/workspacesApi.js.map +1 -0
  17. package/dist/flows/errors.d.ts +12 -0
  18. package/dist/flows/errors.d.ts.map +1 -0
  19. package/dist/flows/errors.js +13 -0
  20. package/dist/flows/errors.js.map +1 -0
  21. package/dist/flows/generated/flows.gen.d.ts +19 -0
  22. package/dist/flows/generated/flows.gen.d.ts.map +1 -0
  23. package/dist/flows/generated/flows.gen.js +23 -0
  24. package/dist/flows/generated/flows.gen.js.map +1 -0
  25. package/dist/headless/AcceptInvitation.d.ts +39 -0
  26. package/dist/headless/AcceptInvitation.d.ts.map +1 -0
  27. package/dist/headless/AcceptInvitation.js +27 -0
  28. package/dist/headless/AcceptInvitation.js.map +1 -0
  29. package/dist/headless/Members.d.ts +47 -0
  30. package/dist/headless/Members.d.ts.map +1 -0
  31. package/dist/headless/Members.js +50 -0
  32. package/dist/headless/Members.js.map +1 -0
  33. package/dist/headless/WorkspaceList.d.ts +38 -0
  34. package/dist/headless/WorkspaceList.d.ts.map +1 -0
  35. package/dist/headless/WorkspaceList.js +33 -0
  36. package/dist/headless/WorkspaceList.js.map +1 -0
  37. package/dist/headless/WorkspacesProvider.d.ts +18 -0
  38. package/dist/headless/WorkspacesProvider.d.ts.map +1 -0
  39. package/dist/headless/WorkspacesProvider.js +17 -0
  40. package/dist/headless/WorkspacesProvider.js.map +1 -0
  41. package/dist/i18n/errorsMap.d.ts +10 -0
  42. package/dist/i18n/errorsMap.d.ts.map +1 -0
  43. package/dist/i18n/errorsMap.js +20 -0
  44. package/dist/i18n/errorsMap.js.map +1 -0
  45. package/dist/i18n/generated/errors.gen.d.ts +347 -0
  46. package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
  47. package/dist/i18n/generated/errors.gen.js +177 -0
  48. package/dist/i18n/generated/errors.gen.js.map +1 -0
  49. package/dist/i18n/generated/errors.ru.gen.d.ts +9 -0
  50. package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
  51. package/dist/i18n/generated/errors.ru.gen.js +61 -0
  52. package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
  53. package/dist/i18n/keys.d.ts +55 -0
  54. package/dist/i18n/keys.d.ts.map +1 -0
  55. package/dist/i18n/keys.js +76 -0
  56. package/dist/i18n/keys.js.map +1 -0
  57. package/dist/i18n/ru.d.ts +28 -0
  58. package/dist/i18n/ru.d.ts.map +1 -0
  59. package/dist/i18n/ru.js +49 -0
  60. package/dist/i18n/ru.js.map +1 -0
  61. package/dist/index.d.ts +38 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +36 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/model/context.d.ts +14 -0
  66. package/dist/model/context.d.ts.map +1 -0
  67. package/dist/model/context.js +21 -0
  68. package/dist/model/context.js.map +1 -0
  69. package/dist/model/mutations.d.ts +55 -0
  70. package/dist/model/mutations.d.ts.map +1 -0
  71. package/dist/model/mutations.js +138 -0
  72. package/dist/model/mutations.js.map +1 -0
  73. package/dist/model/queries.d.ts +21 -0
  74. package/dist/model/queries.d.ts.map +1 -0
  75. package/dist/model/queries.js +41 -0
  76. package/dist/model/queries.js.map +1 -0
  77. package/dist/model/queryKeys.d.ts +7 -0
  78. package/dist/model/queryKeys.d.ts.map +1 -0
  79. package/dist/model/queryKeys.js +15 -0
  80. package/dist/model/queryKeys.js.map +1 -0
  81. package/dist/model/runtime.d.ts +26 -0
  82. package/dist/model/runtime.d.ts.map +1 -0
  83. package/dist/model/runtime.js +18 -0
  84. package/dist/model/runtime.js.map +1 -0
  85. package/llms.txt +95 -0
  86. package/manifest.json +705 -0
  87. package/package.json +85 -0
  88. package/src/analytics/generated/events.json +7 -0
  89. package/src/api/extensions.ts +7 -0
  90. package/src/api/types.ts +58 -0
  91. package/src/api/workspacesApi.ts +139 -0
  92. package/src/flows/errors.ts +16 -0
  93. package/src/flows/generated/flows.gen.ts +38 -0
  94. package/src/flows/generated/flows.json +1 -0
  95. package/src/headless/AcceptInvitation.tsx +53 -0
  96. package/src/headless/Members.tsx +89 -0
  97. package/src/headless/WorkspaceList.tsx +58 -0
  98. package/src/headless/WorkspacesProvider.tsx +25 -0
  99. package/src/i18n/errorsMap.ts +31 -0
  100. package/src/i18n/generated/errors.gen.ts +208 -0
  101. package/src/i18n/generated/errors.json +394 -0
  102. package/src/i18n/generated/errors.ru.gen.ts +67 -0
  103. package/src/i18n/keys.ts +83 -0
  104. package/src/i18n/ru.ts +53 -0
  105. package/src/index.ts +113 -0
  106. package/src/model/context.tsx +29 -0
  107. package/src/model/mutations.ts +196 -0
  108. package/src/model/queries.ts +51 -0
  109. package/src/model/queryKeys.ts +20 -0
  110. package/src/model/runtime.ts +46 -0
  111. package/tsconfig.json +26 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,60 @@
1
+ # @stapel/workspaces-react
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0786d55: Russian locale as an opt-in `@stapel/workspaces-react/i18n/ru` subpath
8
+ (i18n-shipping wave 2, following the auth-react etalon — wave 1).
9
+
10
+ - `errors.ru.gen.ts` — generated per-locale error bundle, auto-discovered by
11
+ the shared `gen-errors.mjs` driver from stapel-workspaces's
12
+ `translations/errors.ru.json` catalog. `pnpm gen:errors:check` remains the
13
+ drift gate; existing en outputs are byte-identical.
14
+ - `@stapel/workspaces-react/i18n/ru` — `workspacesI18nBundleRu` (generated
15
+ backend ru + hand-written ru UI copy) and `registerWorkspacesI18nRu(engine)`,
16
+ which registers the en floor UNDER the ru texts so a missing key degrades
17
+ to English, never to a raw key. Host bundles registered after the pair's
18
+ win (merge-priority convention, now documented on `registerWorkspacesI18n`).
19
+ - Tree-shake purity is gated twice: the main-entry size-limit budget is
20
+ unchanged (the ru locale is not in its graph; the ru subpath is its own
21
+ chunk with its own budget) and `test/i18nRu.test.ts` walks the compiled
22
+ `dist/index.js` module graph asserting the ru modules never appear.
23
+
24
+ - 1af230c: New headless React flow pair for **stapel-workspaces** — the fourth pipeline pair
25
+ (final of the first wave) after notifications, profiles, and billing (scaffolded
26
+ by `stapel-new-react-lib`, tools 0.8.2). Business + state only, zero visual
27
+ opinion, built on `@stapel/core`'s StapelClient.
28
+
29
+ - **API surface (`workspacesApi`)** — ten typed operations over the signed-in
30
+ workspaces endpoints: `listWorkspaces` / `createWorkspace` / `getWorkspace` /
31
+ `updateWorkspace` / `deleteWorkspace` / `listMembers` / `inviteMembers` /
32
+ `updateMemberRole` / `removeMember` / `acceptInvitation`. Wire types alias the
33
+ generated `@stapel/core` schema (two documented corrections: `WorkspaceRole`
34
+ and `WorkspaceKind` narrow the backend's bare `role` / `type` strings to their
35
+ `TextChoices`). The service-to-service `GET /internal/{ws}/members/{user}` and
36
+ `POST /internal/users/{user}/personal` are intentionally excluded —
37
+ machine-to-machine surfaces, not part of the signed-in UI.
38
+ - **Model hooks** — read hooks `useWorkspaces` / `useWorkspace` / `useMembers`
39
+ and write hooks `useCreateWorkspace` / `useUpdateWorkspace` /
40
+ `useDeleteWorkspace` / `useInviteMembers` / `useUpdateMemberRole` /
41
+ `useRemoveMember` / `useAcceptInvitation`, all under the namespaced
42
+ `workspacesQueryKeys`. Membership and ownership are server truth (roles gate
43
+ access cross-service via the membership cache), so no mutation is optimistic
44
+ (frontend-core-architecture §2.6).
45
+ - **Headless components** — `WorkspaceList` (the caller's workspaces + create),
46
+ `Members` (roster + invite / role-change / removal for one workspace), and
47
+ `AcceptInvitation` (join by email-link token), plus the `WorkspacesProvider`
48
+ root. Each ships a demo (completeness gate green) and msw happy-path tests,
49
+ including a negative case that surfaces a localizable
50
+ `error.400.invitation_expired`.
51
+ - **i18n** — an English `workspaces.*` key bundle spread over the generated
52
+ backend error fallbacks, so a `StapelApiError.code` never renders as a raw key.
53
+
54
+ ## 0.0.0
55
+
56
+ - Scaffolded by `stapel-new-react-lib` from the auth-react etalon
57
+ (frontend-standard §9, frontend-core-architecture §4 checklist). Layers
58
+ api → model → flows → headless → i18n; drift-gated generated surfaces
59
+ (flows registry, backend error map, manifest + llms.txt) via the shared
60
+ monorepo `gen:*` drivers.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stapel contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/MODULE.md ADDED
@@ -0,0 +1,54 @@
1
+ # @stapel/workspaces-react — module guide
2
+
3
+ Headless React flow pair for **stapel-workspaces**. This is the human companion to the
4
+ generated `llms.txt` (agent context) and `manifest.json` (machine catalog).
5
+
6
+ ## Layers
7
+
8
+ - **api/** — `createWorkspacesApi(client)`; types are aliases over the generated
9
+ `components["schemas"]` from `@stapel/core` (never parallel hand-written
10
+ bodies). Named typed operations arrive with gen-api v2 (`core-typed-ops`);
11
+ hand-authored, un-generatable surface lives in `api/extensions.ts`.
12
+ - **model/** — `workspacesQueryKeys` (single key factory, `["workspaces"]`
13
+ namespace), `createWorkspacesRuntime`, React context/hooks. Declare the
14
+ persist/optimistic policy here as you add read hooks and mutations.
15
+ - **flows/** — `createFlowMachine`-based machines (primitive imported from
16
+ `@stapel/core`), bound to the generated `WORKSPACES_FLOWS` registry. Scaffold
17
+ new machines from flows.json; keep them under `gen:flows:check`.
18
+ - **headless/** — render-prop components; `<WorkspacesProvider>` wires the
19
+ runtime into context. shadcn-copyable (frontend-standard §7).
20
+ - **i18n/** — `WORKSPACES_I18N_KEYS` + en bundle; the generated backend error
21
+ bundle is merged in so every `error.*` code has a fallback. Override any key
22
+ via core's `loadLocale`, or statically by registering a host bundle AFTER
23
+ the pair's (registration order = override priority, later wins per key —
24
+ i18n-shipping.md §3). Layering inside one locale: generated en floor → pair
25
+ polish/UI copy → pair locale bundle (`@stapel/workspaces-react/i18n/ru`:
26
+ generated from the backend's `translations/errors.ru.json` catalog +
27
+ hand-written ru UI copy; it registers the en floor UNDER ru so a missing key
28
+ degrades to English, never a raw key) → host bundle last. The ru subpath is
29
+ opt-in and stays out of the main entry (size-limit budget + module-graph
30
+ test in `test/i18nRu.test.ts`).
31
+ - **analytics/** — `generated/events.json`, the typed-event registry projected
32
+ from `defineEvent` call sites + flow funnels (`pnpm gen:events`). Read by the
33
+ analytics lint and embedded into `manifest.json`; nothing to hand-edit.
34
+ - **demo/** — first-class demos (`defineDemo`, `@stapel/showcase`): `_harness.tsx`
35
+ wires a mock runtime + i18n + query client; each `<Name>.demo.tsx` is compiled,
36
+ product-linted, smoke-rendered, and projected to a Ladle story (`pnpm gen:demos`).
37
+ The completeness gate requires ≥1 demo per exported headless component; the
38
+ starter `Workspaces.demo.tsx` covers `WorkspacesProvider`. Demos never ship.
39
+
40
+ ## Extension seams (frontend-standard §7)
41
+
42
+ - Client is injected via `<WorkspacesProvider>` / core's `StapelConfigProvider`
43
+ (per-module override) — pairs never hard-import a client.
44
+ - Flow deps are injected through `create<X>Flow(deps)` factories.
45
+ - The headless layer is fully replaceable (copy-and-own).
46
+
47
+ ## TODO after scaffold
48
+
49
+ 1. `pnpm install && pnpm gen` — materialize the generated surfaces.
50
+ 2. Alias the stapel-workspaces schemas you use in `api/types.ts`.
51
+ 3. Add read hooks + mutations in `model/` and a persist/optimistic policy.
52
+ 4. Once stapel-workspaces annotates `@flow_step`, scaffold flow machines from
53
+ flows.json and put them under `gen:flows:check`.
54
+ 5. Fill `MODULE.md`'s machine table and link the SA-doc flows.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # @stapel/workspaces-react
2
+
3
+ Headless React flow pair for stapel-workspaces: typed API client, TanStack Query hooks, flow machines, headless components, and i18n keys. Zero visual opinion.
4
+
5
+ Headless React flow pair for **stapel-workspaces** (frontend-standard §2). Business +
6
+ state only, zero visual opinion — any design layers on top. Built on
7
+ `@stapel/core` (typed client + `StapelApiError` envelope, token refresh,
8
+ verification-403 interception, i18n engine, analytics facade, TanStack Query).
9
+
10
+ Scaffolded by `stapel-new-react-lib`. See `MODULE.md` for the layer map, machine
11
+ table, extension seams, and persist policy.
12
+
13
+ ## Layers
14
+
15
+ ```
16
+ src/
17
+ api/ typed client — thin adapter over @stapel/core `components`
18
+ model/ query keys, runtime wiring, context/hooks
19
+ flows/ createFlowMachine flow machines (+ generated registry)
20
+ headless/ renderless components (WorkspacesProvider, flow render-props)
21
+ i18n/ translation keys + generated backend error map
22
+ analytics/ generated typed-event registry (events.json)
23
+ demo/ first-class demos (compiled, product-linted, smoke-rendered)
24
+ ```
25
+
26
+ ## Generated surfaces (drift-gated)
27
+
28
+ | Surface | Path | Gate |
29
+ |---|---|---|
30
+ | Flow registry | `src/flows/generated/` | `pnpm gen:flows:check` |
31
+ | Backend error map + en bundle | `src/i18n/generated/` | `pnpm gen:errors:check` |
32
+ | Typed-event registry | `src/analytics/generated/events.json` | `pnpm gen:events:check` |
33
+ | Demos → Ladle stories | `demo/generated/` | `pnpm gen:demos:check` |
34
+ | `manifest.json` + `llms.txt` | package root | `pnpm gen:manifest:check` |
35
+
36
+ These drift gates run at the **monorepo root** (`pnpm gen` / `pnpm gen:check`) —
37
+ the etalon's env-parametrized `scripts/gen-*.mjs` drivers are shared, not forked.
38
+ `stapel-new-react-lib` wired this pair into the root `gen`/`gen:check` aggregates
39
+ at scaffold time (one env-parametrized invocation per driver). The typed
40
+ `schema.ts` is core-owned (`pnpm gen:api`); design tokens are tokens-owned
41
+ (`pnpm gen:tokens`).
42
+
43
+ ### Russian locale (opt-in subpath)
44
+
45
+ The `ru` bundle ships as a separate subpath so it never bloats the main entry
46
+ (size-limit gated — the locale stays out of hosts that don't register it):
47
+
48
+ ```tsx
49
+ import { registerWorkspacesI18nRu } from "@stapel/workspaces-react/i18n/ru";
50
+
51
+ registerWorkspacesI18n(i18n); // en floor + polish
52
+ registerWorkspacesI18nRu(i18n); // ru locale (generated from the backend catalog)
53
+ await i18n.setLocale("ru"); // live switch; a missing key degrades to English
54
+ ```
55
+
56
+ Backend error texts are generated from stapel-workspaces's
57
+ `translations/errors.ru.json` catalog (`pnpm gen:errors`, drift-gated); the
58
+ pair's UI keys carry hand-written ru copy. Register your own bundle AFTER the
59
+ pair's to override any key — registration order is override priority.
60
+
61
+ ## Guardrails
62
+
63
+ Linted by the shared `@stapel/eslint-plugin` flat config (no raw colours, no raw
64
+ token imports, no raw fetch, i18n-key existence, typed analytics, headless-only)
65
+ and the shared **stylelint** preset — `pnpm lint` per package plus `pnpm lint:css`
66
+ at the root (colours only ever `var(--stapel-*)`). Demos are first-class code:
67
+ compiled by `tsconfig.demo.json`, linted with the product ruleset, and
68
+ smoke-rendered by `test/demos.test.tsx` — but never shipped (excluded from the
69
+ `files` allowlist; proven by `test/prodBundlePurity.test.ts`).
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Hand-authored API surface the codegen does not (yet) cover — browser-redirect
3
+ * URL builders, open-redirect guards, narrow domain type-guards, header
4
+ * conventions. Everything that CAN be derived from schema.json belongs in the
5
+ * generated operations (`api/workspacesApi.ts`), not here.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../../src/api/extensions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Hand-authored API surface the codegen does not (yet) cover — browser-redirect
3
+ * URL builders, open-redirect guards, narrow domain type-guards, header
4
+ * conventions. Everything that CAN be derived from schema.json belongs in the
5
+ * generated operations (`api/workspacesApi.ts`), not here.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../src/api/extensions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Wire types for the stapel-workspaces HTTP contract — **derived from the generated
3
+ * OpenAPI surface** (frontend-standard §2/§3), never hand-maintained. The
4
+ * single source of truth is `components["schemas"]` from `@stapel/core`
5
+ * (`packages/core/src/generated/schema.ts`, produced by `pnpm gen:api` from the
6
+ * unified all-modules OpenAPI). Alias the schemas this pair uses under local
7
+ * names here; do NOT write parallel response bodies. Where drf-spectacular +
8
+ * openapi-typescript under-describe the runtime, apply a small documented
9
+ * correction (see auth-react `api/types.ts` for the three canonical patterns).
10
+ */
11
+ import type { components } from "@stapel/core";
12
+ /** The generated schema table — the one source of truth for wire shapes. */
13
+ export type Schemas = components["schemas"];
14
+ /** One workspace — identity, storage, my role, member count (GET/POST/PATCH). */
15
+ export type Workspace = Schemas["WorkspaceResponse"];
16
+ /** GET / 200 body — the caller's workspaces (memberships). */
17
+ export type WorkspaceList = Schemas["WorkspaceListResponse"];
18
+ /** POST / request body — create a workspace (slug auto-generated when omitted). */
19
+ export type WorkspaceCreate = Schemas["WorkspaceCreateRequest"];
20
+ /** PATCH /{id} request body — a partial name / slug / settings update. */
21
+ export type WorkspaceUpdate = Schemas["PatchedWorkspaceUpdateRequest"];
22
+ /** One workspace member (GET members, PATCH member role, POST accept). */
23
+ export type Member = Schemas["MemberResponse"];
24
+ /** GET /{id}/members 200 body — a workspace's members. */
25
+ export type MemberList = Schemas["MemberListResponse"];
26
+ /** POST /{id}/members/invite request body — one or more emails + a role. */
27
+ export type MemberInvite = Schemas["MemberInviteRequest"];
28
+ /** POST /{id}/members/invite 201 body — the created invitations. */
29
+ export type MemberInviteResult = Schemas["MemberInviteResponse"];
30
+ /** PATCH /{id}/members/{userId} request body — the new role. */
31
+ export type MemberRoleUpdate = Schemas["PatchedMemberUpdateRequest"];
32
+ /** One pending/accepted invitation (a row of {@link MemberInviteResult}). */
33
+ export type Invitation = Schemas["InvitationResponse"];
34
+ /** POST /invitations/accept request body — the token from the email link. */
35
+ export type InvitationAccept = Schemas["InvitationAcceptRequest"];
36
+ /**
37
+ * A workspace membership role, ordered least→most privileged. The generated
38
+ * schema types `MemberResponse.role` / the invite `role` as a bare `string`,
39
+ * but the backend (`models.Role`, a Django `TextChoices`) constrains it to
40
+ * exactly these values. Narrowing here gives call sites a checked union — a
41
+ * documented correction of the same kind auth-react/billing-react apply.
42
+ */
43
+ export type WorkspaceRole = "owner" | "admin" | "member" | "viewer";
44
+ /**
45
+ * A workspace category. The generated schema types `WorkspaceResponse.type` /
46
+ * the create `type` as a bare `string`; the backend (`models.WorkspaceType`)
47
+ * constrains it to exactly these two. Narrowed for the same reason as
48
+ * {@link WorkspaceRole}.
49
+ */
50
+ export type WorkspaceKind = "personal" | "work";
51
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,4EAA4E;AAC5E,MAAM,MAAM,OAAO,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;AAI5C,iFAAiF;AACjF,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AACrD,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAC7D,mFAAmF;AACnF,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;AAChE,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,+BAA+B,CAAC,CAAC;AACvE,0EAA0E;AAC1E,MAAM,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAC/C,0DAA0D;AAC1D,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AACvD,4EAA4E;AAC5E,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;AAC1D,oEAAoE;AACpE,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACjE,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;AACrE,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AACvD,6EAA6E;AAC7E,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;AAIlE;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ import type { StapelClient } from "@stapel/core";
2
+ import type { Member, MemberInvite, MemberInviteResult, MemberList, MemberRoleUpdate, InvitationAccept, Workspace, WorkspaceCreate, WorkspaceList, WorkspaceUpdate } from "./types.js";
3
+ /**
4
+ * The pair's typed operation surface — one method per stapel-workspaces endpoint
5
+ * a signed-in browser client may call, bound to the injected {@link StapelClient}
6
+ * (the per-module override seam of frontend-standard §7.2). Paths are relative
7
+ * to the runtime's `baseUrl` (e.g. `/workspaces/api`).
8
+ *
9
+ * The service-to-service `GET /internal/{ws}/members/{user}` (membership check
10
+ * from another backend) and `POST /internal/users/{user}/personal` (get-or-create
11
+ * a personal workspace on first login) are intentionally absent — they are
12
+ * machine-to-machine surfaces (`IsServiceRequest | IsStaffUser`), not part of the
13
+ * signed-in UI this pair drives.
14
+ *
15
+ * These operations will be GENERATED from schema.json operationIds by gen-api
16
+ * v2 (task `core-typed-ops`); until then they are hand-authored here (the ONE
17
+ * legal home of path strings — `stapel/no-string-paths` §2.3 carve-out).
18
+ */
19
+ export interface WorkspacesApi {
20
+ readonly client: StapelClient;
21
+ /** The caller's workspaces (accepted memberships, newest-accessed first). */
22
+ listWorkspaces(): Promise<WorkspaceList>;
23
+ /** Create a workspace and seed the caller as owner. */
24
+ createWorkspace(body: WorkspaceCreate): Promise<Workspace>;
25
+ /** A single workspace by id (touches last-accessed). */
26
+ getWorkspace(workspaceId: string): Promise<Workspace>;
27
+ /** Partially update a workspace's name / slug / settings (admin+). */
28
+ updateWorkspace(workspaceId: string, patch: WorkspaceUpdate): Promise<Workspace>;
29
+ /** Soft-delete a workspace (owner only). */
30
+ deleteWorkspace(workspaceId: string): Promise<void>;
31
+ /** A workspace's members (viewer+). */
32
+ listMembers(workspaceId: string): Promise<MemberList>;
33
+ /** Invite one or more emails to a workspace at a role (admin+). */
34
+ inviteMembers(workspaceId: string, body: MemberInvite): Promise<MemberInviteResult>;
35
+ /** Change a member's role (admin+; owner grants require owner). */
36
+ updateMemberRole(workspaceId: string, userId: string, patch: MemberRoleUpdate): Promise<Member>;
37
+ /** Remove a member from a workspace (admin+; the last owner is protected). */
38
+ removeMember(workspaceId: string, userId: string): Promise<void>;
39
+ /** Accept an invitation by its token — returns the caller's new membership. */
40
+ acceptInvitation(body: InvitationAccept): Promise<Member>;
41
+ }
42
+ export declare function createWorkspacesApi(client: StapelClient): WorkspacesApi;
43
+ //# sourceMappingURL=workspacesApi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspacesApi.d.ts","sourceRoot":"","sources":["../../src/api/workspacesApi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,cAAc,CAAC;AACvE,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AAqBpB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAE9B,6EAA6E;IAC7E,cAAc,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IACzC,uDAAuD;IACvD,eAAe,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC3D,wDAAwD;IACxD,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACtD,sEAAsE;IACtE,eAAe,CACb,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,SAAS,CAAC,CAAC;IACtB,4CAA4C;IAC5C,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpD,uCAAuC;IACvC,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtD,mEAAmE;IACnE,aAAa,CACX,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC/B,mEAAmE;IACnE,gBAAgB,CACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,8EAA8E;IAC9E,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjE,+EAA+E;IAC/E,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAoDvE"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * CSRF rule for cookie-authenticated browser clients (mirrors auth-react): the
3
+ * simplest SPA rule is to always send `X-Requested-With: XMLHttpRequest` on
4
+ * mutating requests. Header-token clients ignore it; it is harmless there, so
5
+ * every mutation carries it.
6
+ */
7
+ const CSRF_HEADERS = {
8
+ "X-Requested-With": "XMLHttpRequest",
9
+ };
10
+ function mutating(options) {
11
+ return {
12
+ ...options,
13
+ headers: { ...CSRF_HEADERS, ...options?.headers },
14
+ };
15
+ }
16
+ export function createWorkspacesApi(client) {
17
+ return {
18
+ client,
19
+ listWorkspaces: () => client.get("/"),
20
+ createWorkspace: (body) => client.post("/", body, mutating()),
21
+ getWorkspace: (workspaceId) => client.get(`/${encodeURIComponent(workspaceId)}`),
22
+ updateWorkspace: (workspaceId, patch) => client.patch(`/${encodeURIComponent(workspaceId)}`, patch, mutating()),
23
+ deleteWorkspace: (workspaceId) => client.delete(`/${encodeURIComponent(workspaceId)}`, mutating()),
24
+ listMembers: (workspaceId) => client.get(`/${encodeURIComponent(workspaceId)}/members`),
25
+ inviteMembers: (workspaceId, body) => client.post(`/${encodeURIComponent(workspaceId)}/members/invite`, body, mutating()),
26
+ updateMemberRole: (workspaceId, userId, patch) => client.patch(`/${encodeURIComponent(workspaceId)}/members/${encodeURIComponent(userId)}`, patch, mutating()),
27
+ removeMember: (workspaceId, userId) => client.delete(`/${encodeURIComponent(workspaceId)}/members/${encodeURIComponent(userId)}`, mutating()),
28
+ acceptInvitation: (body) => client.post("/invitations/accept", body, mutating()),
29
+ };
30
+ }
31
+ //# sourceMappingURL=workspacesApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspacesApi.js","sourceRoot":"","sources":["../../src/api/workspacesApi.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,YAAY,GAA2B;IAC3C,kBAAkB,EAAE,gBAAgB;CACrC,CAAC;AAEF,SAAS,QAAQ,CACf,OAAuD;IAEvD,OAAO;QACL,GAAG,OAAO;QACV,OAAO,EAAE,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE;KAClD,CAAC;AACJ,CAAC;AAuDD,MAAM,UAAU,mBAAmB,CAAC,MAAoB;IACtD,OAAO;QACL,MAAM;QAEN,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QAErC,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CACxB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,IAA8B,EAAE,QAAQ,EAAE,CAAC;QAE9D,YAAY,EAAE,CAAC,WAAW,EAAE,EAAE,CAC5B,MAAM,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;QAEnD,eAAe,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE,CACtC,MAAM,CAAC,KAAK,CACV,IAAI,kBAAkB,CAAC,WAAW,CAAC,EAAE,EACrC,KAA+B,EAC/B,QAAQ,EAAE,CACX;QAEH,eAAe,EAAE,CAAC,WAAW,EAAE,EAAE,CAC/B,MAAM,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC;QAElE,WAAW,EAAE,CAAC,WAAW,EAAE,EAAE,CAC3B,MAAM,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,WAAW,CAAC,UAAU,CAAC;QAE3D,aAAa,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,CACnC,MAAM,CAAC,IAAI,CACT,IAAI,kBAAkB,CAAC,WAAW,CAAC,iBAAiB,EACpD,IAA2B,EAC3B,QAAQ,EAAE,CACX;QAEH,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAC/C,MAAM,CAAC,KAAK,CACV,IAAI,kBAAkB,CAAC,WAAW,CAAC,YAAY,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAC3E,KAAgC,EAChC,QAAQ,EAAE,CACX;QAEH,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CACpC,MAAM,CAAC,MAAM,CACX,IAAI,kBAAkB,CAAC,WAAW,CAAC,YAAY,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAC3E,QAAQ,EAAE,CACX;QAEH,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CACzB,MAAM,CAAC,IAAI,CACT,qBAAqB,EACrB,IAA+B,EAC/B,QAAQ,EAAE,CACX;KACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { FlowError } from "@stapel/core";
2
+ export type { FlowError } from "@stapel/core";
3
+ export { isErrorCode } from "@stapel/core";
4
+ /**
5
+ * Fold any thrown value into a {@link FlowError} using this pair's own
6
+ * module-scoped fallback key (`workspaces.error.unknown`, an en string in
7
+ * {@link workspacesI18nBundleEn}) so a non-`StapelApiError` fault still renders
8
+ * real copy rather than a raw key. The primitive lives in `@stapel/core`
9
+ * (frontend-core-architecture §4b); this wrapper only pins the fallback.
10
+ */
11
+ export declare function toFlowError(error: unknown): FlowError;
12
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/flows/errors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAErD"}
@@ -0,0 +1,13 @@
1
+ import { toFlowError as coreToFlowError } from "@stapel/core";
2
+ export { isErrorCode } from "@stapel/core";
3
+ /**
4
+ * Fold any thrown value into a {@link FlowError} using this pair's own
5
+ * module-scoped fallback key (`workspaces.error.unknown`, an en string in
6
+ * {@link workspacesI18nBundleEn}) so a non-`StapelApiError` fault still renders
7
+ * real copy rather than a raw key. The primitive lives in `@stapel/core`
8
+ * (frontend-core-architecture §4b); this wrapper only pins the fallback.
9
+ */
10
+ export function toFlowError(error) {
11
+ return coreToFlowError(error, "workspaces.error.unknown");
12
+ }
13
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/flows/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,cAAc,CAAC;AAI9D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,eAAe,CAAC,KAAK,EAAE,0BAA0B,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The canonical workspaces flow registry — the machine-facing projection of the
3
+ * flow source (docs/flow-system.md §5). Each entry carries a flow's canonical
4
+ * id (`flow.<id>.<step>` analytics funnel = this flow), its i18n keys, and its
5
+ * ordered step→endpoint contract. Flow machines bind their `id` to
6
+ * `WORKSPACES_FLOWS[...].id` so a backend flow rename/re-step surfaces as a drift
7
+ * here (`pnpm gen:flows:check`) rather than a silent analytics/contract skew.
8
+ */
9
+ export declare const WORKSPACES_FLOWS: {};
10
+ /** Canonical flow ids present in flows.json (a subset grows as the backend annotates more). */
11
+ export type WorkspacesFlowId = keyof typeof WORKSPACES_FLOWS;
12
+ export type WorkspacesFlowSpec = (typeof WORKSPACES_FLOWS)[WorkspacesFlowId];
13
+ export interface FlowEndpoint {
14
+ readonly method: string;
15
+ readonly path: string;
16
+ }
17
+ /** All HTTP endpoints a flow touches, in step order (for the contract test / MSW). */
18
+ export declare function flowEndpoints(id: WorkspacesFlowId): readonly FlowEndpoint[];
19
+ //# sourceMappingURL=flows.gen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flows.gen.d.ts","sourceRoot":"","sources":["../../../src/flows/generated/flows.gen.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,IAEnB,CAAC;AAEX,+FAA+F;AAC/F,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,gBAAgB,CAAC;AAE7D,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,gBAAgB,CAAC,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,EAAE,EAAE,gBAAgB,GAAG,SAAS,YAAY,EAAE,CAU3E"}
@@ -0,0 +1,23 @@
1
+ // AUTO-GENERATED by scripts/gen-flows.mjs — do not edit by hand.
2
+ // Source: unified Stapel flows.json (stapel-example-monolith `make codegen`).
3
+ // Regenerate: pnpm gen:flows · Drift gate: pnpm gen:flows:check
4
+ /**
5
+ * The canonical workspaces flow registry — the machine-facing projection of the
6
+ * flow source (docs/flow-system.md §5). Each entry carries a flow's canonical
7
+ * id (`flow.<id>.<step>` analytics funnel = this flow), its i18n keys, and its
8
+ * ordered step→endpoint contract. Flow machines bind their `id` to
9
+ * `WORKSPACES_FLOWS[...].id` so a backend flow rename/re-step surfaces as a drift
10
+ * here (`pnpm gen:flows:check`) rather than a silent analytics/contract skew.
11
+ */
12
+ export const WORKSPACES_FLOWS = {};
13
+ /** All HTTP endpoints a flow touches, in step order (for the contract test / MSW). */
14
+ export function flowEndpoints(id) {
15
+ // Guard the empty-registry case: a module with no annotated flows makes
16
+ // `WorkspacesFlowId` = `never` and `WORKSPACES_FLOWS` = `{}`, so a direct
17
+ // `WORKSPACES_FLOWS[id].steps` does not type-check. Widening to an optional spec
18
+ // keeps this body valid for the zero-flow scaffold AND correct once the
19
+ // backend annotates `@flow_step` and the registry fills in.
20
+ const spec = WORKSPACES_FLOWS[id];
21
+ return spec ? spec.steps.flatMap((s) => s.endpoints) : [];
22
+ }
23
+ //# sourceMappingURL=flows.gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flows.gen.js","sourceRoot":"","sources":["../../../src/flows/generated/flows.gen.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,8EAA8E;AAC9E,oEAAoE;AAEpE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAEtB,CAAC;AAYX,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,EAAoB;IAChD,wEAAwE;IACxE,0EAA0E;IAC1E,iFAAiF;IACjF,wEAAwE;IACxE,4DAA4D;IAC5D,MAAM,IAAI,GAAG,gBAAgB,CAAC,EAAE,CAEnB,CAAC;IACd,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { ReactNode } from "react";
2
+ import type { StapelApiError } from "@stapel/core";
3
+ import type { Member } from "../api/types.js";
4
+ /** Render-prop bag for {@link AcceptInvitation}. */
5
+ export interface AcceptInvitationBag {
6
+ /** Accept the invitation identified by `token`. */
7
+ accept(token: string): void;
8
+ /** An accept call is in flight. */
9
+ readonly isAccepting: boolean;
10
+ /** The accept succeeded — the caller's new membership. */
11
+ readonly isAccepted: boolean;
12
+ /** The membership created on success, else null. */
13
+ readonly member: Member | null;
14
+ /** The accept failed. */
15
+ readonly isError: boolean;
16
+ /**
17
+ * The error, when `isError`. A dead token surfaces a localizable
18
+ * `StapelApiError` — `error.400.invitation_expired` /
19
+ * `error.400.invitation_revoked` / `error.400.invitation_already_used` /
20
+ * `error.404.invitation_not_found` — each carrying its backend remediation.
21
+ */
22
+ readonly error: StapelApiError | null;
23
+ }
24
+ /**
25
+ * Headless invitation-accept control — a renderless "join by token" action.
26
+ * Wires {@link useAcceptInvitation} and hands an {@link AcceptInvitationBag} to
27
+ * `children`; bring your own accept button / status. Zero visual opinion
28
+ * (frontend-standard §2).
29
+ *
30
+ * ```tsx
31
+ * <AcceptInvitation>
32
+ * {({ accept, isAccepted, error }) => ( ... )}
33
+ * </AcceptInvitation>
34
+ * ```
35
+ */
36
+ export declare function AcceptInvitation(props: {
37
+ children: (bag: AcceptInvitationBag) => ReactNode;
38
+ }): ReactNode;
39
+ //# sourceMappingURL=AcceptInvitation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AcceptInvitation.d.ts","sourceRoot":"","sources":["../../src/headless/AcceptInvitation.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAG9C,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,mDAAmD;IACnD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mCAAmC;IACnC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,SAAS,CAAC;CACnD,GAAG,SAAS,CAYZ"}
@@ -0,0 +1,27 @@
1
+ import { useAcceptInvitation } from "../model/mutations.js";
2
+ /**
3
+ * Headless invitation-accept control — a renderless "join by token" action.
4
+ * Wires {@link useAcceptInvitation} and hands an {@link AcceptInvitationBag} to
5
+ * `children`; bring your own accept button / status. Zero visual opinion
6
+ * (frontend-standard §2).
7
+ *
8
+ * ```tsx
9
+ * <AcceptInvitation>
10
+ * {({ accept, isAccepted, error }) => ( ... )}
11
+ * </AcceptInvitation>
12
+ * ```
13
+ */
14
+ export function AcceptInvitation(props) {
15
+ const mutation = useAcceptInvitation();
16
+ return props.children({
17
+ accept: (token) => {
18
+ mutation.mutate({ token });
19
+ },
20
+ isAccepting: mutation.isPending,
21
+ isAccepted: mutation.isSuccess,
22
+ member: mutation.data ?? null,
23
+ isError: mutation.isError,
24
+ error: mutation.error ?? null,
25
+ });
26
+ }
27
+ //# sourceMappingURL=AcceptInvitation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AcceptInvitation.js","sourceRoot":"","sources":["../../src/headless/AcceptInvitation.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAuB5D;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAEhC;IACC,MAAM,QAAQ,GAAG,mBAAmB,EAAE,CAAC;IACvC,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChB,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,WAAW,EAAE,QAAQ,CAAC,SAAS;QAC/B,UAAU,EAAE,QAAQ,CAAC,SAAS;QAC9B,MAAM,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI;QAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,IAAI;KAC9B,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { ReactNode } from "react";
2
+ import type { StapelApiError } from "@stapel/core";
3
+ import type { Member, MemberInvite } from "../api/types.js";
4
+ import type { MemberRoleChange } from "../model/mutations.js";
5
+ /** Render-prop bag for {@link Members}. */
6
+ export interface MembersBag {
7
+ /** The workspace's members once loaded (empty before load). */
8
+ readonly members: readonly Member[];
9
+ /** The initial member-list load is in flight. */
10
+ readonly isLoading: boolean;
11
+ /** The read, an invite, a role change, or a removal failed. */
12
+ readonly isError: boolean;
13
+ /** The error, when `isError` (a localizable `StapelApiError`), else null. */
14
+ readonly error: StapelApiError | null;
15
+ /** Invite one or more emails at a role. */
16
+ invite(body: MemberInvite): void;
17
+ /** An invite call is in flight. */
18
+ readonly isInviting: boolean;
19
+ /** Change a member's role. */
20
+ updateRole(change: MemberRoleChange): void;
21
+ /** A role-change call is in flight. */
22
+ readonly isUpdatingRole: boolean;
23
+ /** Remove a member by `userId`. */
24
+ remove(userId: string): void;
25
+ /** A removal call is in flight. */
26
+ readonly isRemoving: boolean;
27
+ /** Refetch the member list from the server. */
28
+ refetch(): void;
29
+ }
30
+ /**
31
+ * Headless member roster — a renderless view + invite / role-change / removal
32
+ * controls over one workspace's members. Wires {@link useMembers},
33
+ * {@link useInviteMembers}, {@link useUpdateMemberRole}, and
34
+ * {@link useRemoveMember} and hands a {@link MembersBag} to `children`; bring your
35
+ * own roster / invite form / role menu. Zero visual opinion (frontend-standard §2).
36
+ *
37
+ * ```tsx
38
+ * <Members workspaceId={id}>
39
+ * {({ members, invite, updateRole, remove }) => ( ... )}
40
+ * </Members>
41
+ * ```
42
+ */
43
+ export declare function Members(props: {
44
+ workspaceId: string;
45
+ children: (bag: MembersBag) => ReactNode;
46
+ }): ReactNode;
47
+ //# sourceMappingURL=Members.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Members.d.ts","sourceRoot":"","sources":["../../src/headless/Members.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAO5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,2CAA2C;IAC3C,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,8BAA8B;IAC9B,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3C,uCAAuC;IACvC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,mCAAmC;IACnC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,+CAA+C;IAC/C,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,SAAS,CAAC;CAC1C,GAAG,SAAS,CAmCZ"}