@stapel/workspaces-react 0.6.0 → 0.8.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 (105) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/api/generated/schema.d.ts +774 -7
  3. package/dist/api/generated/schema.d.ts.map +1 -1
  4. package/dist/api/types.d.ts +104 -7
  5. package/dist/api/types.d.ts.map +1 -1
  6. package/dist/api/workspacesApi.d.ts +55 -1
  7. package/dist/api/workspacesApi.d.ts.map +1 -1
  8. package/dist/api/workspacesApi.js +23 -0
  9. package/dist/api/workspacesApi.js.map +1 -1
  10. package/dist/default/InviteAcceptPage.d.ts +28 -0
  11. package/dist/default/InviteAcceptPage.d.ts.map +1 -0
  12. package/dist/default/InviteAcceptPage.js +141 -0
  13. package/dist/default/InviteAcceptPage.js.map +1 -0
  14. package/dist/default/MembersManager.d.ts +4 -4
  15. package/dist/default/MembersManager.d.ts.map +1 -1
  16. package/dist/default/MembersManager.js +61 -56
  17. package/dist/default/MembersManager.js.map +1 -1
  18. package/dist/default/index.d.ts +2 -0
  19. package/dist/default/index.d.ts.map +1 -1
  20. package/dist/default/index.js +1 -0
  21. package/dist/default/index.js.map +1 -1
  22. package/dist/flows/inviteAcceptFlow.d.ts +155 -0
  23. package/dist/flows/inviteAcceptFlow.d.ts.map +1 -0
  24. package/dist/flows/inviteAcceptFlow.js +126 -0
  25. package/dist/flows/inviteAcceptFlow.js.map +1 -0
  26. package/dist/headless/Can.d.ts +40 -0
  27. package/dist/headless/Can.d.ts.map +1 -0
  28. package/dist/headless/Can.js +30 -0
  29. package/dist/headless/Can.js.map +1 -0
  30. package/dist/headless/InviteAcceptFlow.d.ts +44 -0
  31. package/dist/headless/InviteAcceptFlow.d.ts.map +1 -0
  32. package/dist/headless/InviteAcceptFlow.js +67 -0
  33. package/dist/headless/InviteAcceptFlow.js.map +1 -0
  34. package/dist/headless/RoleSelect.d.ts +41 -0
  35. package/dist/headless/RoleSelect.d.ts.map +1 -0
  36. package/dist/headless/RoleSelect.js +34 -0
  37. package/dist/headless/RoleSelect.js.map +1 -0
  38. package/dist/i18n/generated/errors.gen.d.ts +48 -0
  39. package/dist/i18n/generated/errors.gen.d.ts.map +1 -1
  40. package/dist/i18n/generated/errors.gen.js +24 -0
  41. package/dist/i18n/generated/errors.gen.js.map +1 -1
  42. package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -1
  43. package/dist/i18n/generated/errors.ru.gen.js +8 -0
  44. package/dist/i18n/generated/errors.ru.gen.js.map +1 -1
  45. package/dist/i18n/keys.d.ts +28 -0
  46. package/dist/i18n/keys.d.ts.map +1 -1
  47. package/dist/i18n/keys.js +64 -0
  48. package/dist/i18n/keys.js.map +1 -1
  49. package/dist/i18n/ru.d.ts.map +1 -1
  50. package/dist/i18n/ru.js +30 -0
  51. package/dist/i18n/ru.js.map +1 -1
  52. package/dist/index.d.ts +17 -4
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +10 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/model/capabilities.d.ts +21 -0
  57. package/dist/model/capabilities.d.ts.map +1 -0
  58. package/dist/model/capabilities.js +32 -0
  59. package/dist/model/capabilities.js.map +1 -0
  60. package/dist/model/emailMask.d.ts +22 -0
  61. package/dist/model/emailMask.d.ts.map +1 -0
  62. package/dist/model/emailMask.js +36 -0
  63. package/dist/model/emailMask.js.map +1 -0
  64. package/dist/model/mutations.d.ts +107 -3
  65. package/dist/model/mutations.d.ts.map +1 -1
  66. package/dist/model/mutations.js +190 -1
  67. package/dist/model/mutations.js.map +1 -1
  68. package/dist/model/queries.d.ts +108 -2
  69. package/dist/model/queries.d.ts.map +1 -1
  70. package/dist/model/queries.js +139 -1
  71. package/dist/model/queries.js.map +1 -1
  72. package/dist/model/queryKeys.d.ts +12 -1
  73. package/dist/model/queryKeys.d.ts.map +1 -1
  74. package/dist/model/queryKeys.js +25 -0
  75. package/dist/model/queryKeys.js.map +1 -1
  76. package/dist/model/stepUp.d.ts +25 -0
  77. package/dist/model/stepUp.d.ts.map +1 -0
  78. package/dist/model/stepUp.js +79 -0
  79. package/dist/model/stepUp.js.map +1 -0
  80. package/llms.txt +49 -15
  81. package/manifest.json +346 -3
  82. package/package.json +5 -5
  83. package/src/analytics/generated/events.json +1 -1
  84. package/src/api/generated/schema.ts +774 -7
  85. package/src/api/types.ts +113 -7
  86. package/src/api/workspacesApi.ts +137 -0
  87. package/src/default/InviteAcceptPage.tsx +373 -0
  88. package/src/default/MembersManager.tsx +155 -145
  89. package/src/default/index.ts +2 -0
  90. package/src/flows/inviteAcceptFlow.ts +289 -0
  91. package/src/headless/Can.tsx +49 -0
  92. package/src/headless/InviteAcceptFlow.tsx +105 -0
  93. package/src/headless/RoleSelect.tsx +58 -0
  94. package/src/i18n/generated/errors.gen.ts +24 -0
  95. package/src/i18n/generated/errors.json +62 -0
  96. package/src/i18n/generated/errors.ru.gen.ts +8 -0
  97. package/src/i18n/keys.ts +68 -0
  98. package/src/i18n/ru.ts +34 -0
  99. package/src/index.ts +54 -1
  100. package/src/model/capabilities.ts +37 -0
  101. package/src/model/emailMask.ts +37 -0
  102. package/src/model/mutations.ts +248 -4
  103. package/src/model/queries.ts +205 -2
  104. package/src/model/queryKeys.ts +45 -1
  105. package/src/model/stepUp.ts +96 -0
@@ -5,73 +5,78 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
5
  * инвайты)"). Built entirely on this pair's EXISTING `Members` headless
6
6
  * wrapper (`useMembers`/`useInviteMembers`/`useUpdateMemberRole`/
7
7
  * `useRemoveMember`) — no new backend surface.
8
+ *
9
+ * Roles come from the EFFECTIVE registry via the `RoleSelect` headless
10
+ * (GET /roles, org-program §A2) — not a hardcoded builtin four: a deployment
11
+ * that overlays `STAPEL_WORKSPACES["ROLES"]` (e.g. a `secretary`) sees its
12
+ * roles here with `workspaces.role.<key>` labels (client-bundle merge,
13
+ * raw-name fallback). `owner` is offered only in the member-row select (the
14
+ * backend enforces "only an owner grants owner" and last-owner protection);
15
+ * the invite dialog filters it out, as before.
8
16
  */
9
17
  import { useState } from "react";
10
18
  import { Alert, Button, Card, Input, Modal, Popconfirm, Select, Table, Typography } from "antd";
11
19
  import { useT } from "@stapel/core";
12
20
  import { Members } from "../headless/Members.js";
21
+ import { RoleSelect } from "../headless/RoleSelect.js";
13
22
  import { WORKSPACES_I18N_KEYS } from "../i18n/keys.js";
14
- const ROLES = ["owner", "admin", "member", "viewer"];
15
- const ROLE_KEY = {
16
- owner: "roleOwner",
17
- admin: "roleAdmin",
18
- member: "roleMember",
19
- viewer: "roleViewer",
20
- };
21
- function isWorkspaceRole(value) {
22
- return ROLES.includes(value);
23
- }
23
+ const DEFAULT_INVITE_ROLE = "member";
24
24
  export function MembersManager(props) {
25
25
  const t = useT();
26
26
  const canManage = props.canManage ?? true;
27
27
  const [inviteOpen, setInviteOpen] = useState(false);
28
28
  const [emailsText, setEmailsText] = useState("");
29
- const [inviteRole, setInviteRole] = useState("member");
30
- return (_jsx(Members, { workspaceId: props.workspaceId, children: ({ members, isLoading, isError, error, invite, isInviting, updateRole, remove }) => {
31
- function openInvite() {
32
- setEmailsText("");
33
- setInviteRole("member");
34
- setInviteOpen(true);
35
- }
36
- function submitInvite() {
37
- const emails = emailsText
38
- .split(/[,\s]+/)
39
- .map((e) => e.trim())
40
- .filter((e) => e.length > 0);
41
- if (emails.length === 0)
42
- return;
43
- invite({ emails, role: inviteRole });
44
- setInviteOpen(false);
45
- }
46
- const columns = [
47
- {
48
- title: "Email",
49
- dataIndex: "email",
50
- key: "email",
51
- render: (value) => value ?? "—",
52
- },
53
- {
54
- title: t(WORKSPACES_I18N_KEYS.membersInviteRoleLabel),
55
- key: "role",
56
- render: (_, member) => {
57
- const role = isWorkspaceRole(member.role) ? member.role : "member";
58
- return canManage ? (_jsx(Select, { value: role, style: { width: 140 }, onChange: (next) => updateRole({ userId: member.user_id, role: next }), options: ROLES.map((r) => ({ value: r, label: t(WORKSPACES_I18N_KEYS[ROLE_KEY[r]]) })) })) : (_jsx("span", { children: t(WORKSPACES_I18N_KEYS[ROLE_KEY[role]]) }));
29
+ const [inviteRole, setInviteRole] = useState(DEFAULT_INVITE_ROLE);
30
+ return (_jsx(RoleSelect, { children: (rolesBag) => (_jsx(Members, { workspaceId: props.workspaceId, children: ({ members, isLoading, isError, error, invite, isInviting, updateRole, remove }) => {
31
+ const { labelFor } = rolesBag;
32
+ const roleOptions = rolesBag.roles.map((r) => ({
33
+ value: r.role,
34
+ label: labelFor(r.role),
35
+ }));
36
+ const inviteRoleOptions = roleOptions.filter((o) => o.value !== "owner");
37
+ function openInvite() {
38
+ setEmailsText("");
39
+ setInviteRole(DEFAULT_INVITE_ROLE);
40
+ setInviteOpen(true);
41
+ }
42
+ function submitInvite() {
43
+ const emails = emailsText
44
+ .split(/[,\s]+/)
45
+ .map((e) => e.trim())
46
+ .filter((e) => e.length > 0);
47
+ if (emails.length === 0)
48
+ return;
49
+ invite({ emails, role: inviteRole });
50
+ setInviteOpen(false);
51
+ }
52
+ const columns = [
53
+ {
54
+ title: "Email",
55
+ dataIndex: "email",
56
+ key: "email",
57
+ render: (value) => value ?? "—",
58
+ },
59
+ {
60
+ title: t(WORKSPACES_I18N_KEYS.membersInviteRoleLabel),
61
+ key: "role",
62
+ render: (_, member) => canManage ? (_jsx(Select, { value: member.role, style: { width: 160 }, loading: rolesBag.isLoading, onChange: (next) => updateRole({ userId: member.user_id, role: next }),
63
+ // A member's CURRENT role may be missing from the
64
+ // registry options (a deployment removed an overlay
65
+ // role) — antd shows the raw value then, which matches
66
+ // labelFor's raw-name fallback contract.
67
+ options: roleOptions })) : (_jsx("span", { children: labelFor(member.role) })),
59
68
  },
60
- },
61
- ...(canManage
62
- ? [
63
- {
64
- title: "",
65
- key: "actions",
66
- render: (_, member) => (_jsx(Popconfirm, { title: t(WORKSPACES_I18N_KEYS.membersRemoveConfirm), onConfirm: () => remove(member.user_id), children: _jsx(Button, { danger: true, type: "link", children: t(WORKSPACES_I18N_KEYS.membersRemove) }) })),
67
- },
68
- ]
69
- : []),
70
- ];
71
- return (_jsxs(Card, { "data-testid": "members-manager", children: [_jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 16 }, children: [_jsxs("div", { children: [_jsx(Typography.Title, { level: 4, style: { marginTop: 0 }, children: t(WORKSPACES_I18N_KEYS.membersTitle) }), _jsx(Typography.Text, { type: "secondary", children: t(WORKSPACES_I18N_KEYS.membersSubtitle) })] }), canManage && (_jsx(Button, { type: "primary", onClick: openInvite, "data-analytics": "none", "data-analytics-reason": "local-ui-open-invite-dialog", children: t(WORKSPACES_I18N_KEYS.membersInvite) }))] }), isError && error && (_jsx(Alert, { style: { marginTop: 12 }, type: "error", showIcon: true, message: error.message })), _jsx(Table, { style: { marginTop: 16 }, size: "small", loading: isLoading, rowKey: (member) => member.id, dataSource: members, columns: columns, pagination: false }), _jsx(Modal, { title: t(WORKSPACES_I18N_KEYS.membersInviteDialogTitle), open: inviteOpen, onCancel: () => setInviteOpen(false), onOk: submitInvite, okButtonProps: { loading: isInviting, disabled: emailsText.trim().length === 0 }, children: _jsxs("div", { style: { display: "grid", gap: 12 }, children: [_jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.membersInviteEmailsLabel) }), _jsx(Input, { value: emailsText, onChange: (e) => setEmailsText(e.target.value), placeholder: t(WORKSPACES_I18N_KEYS.membersInviteEmailsPlaceholder) })] }), _jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.membersInviteRoleLabel) }), _jsx(Select, { value: inviteRole, onChange: setInviteRole, style: { width: "100%" }, options: ROLES.filter((r) => r !== "owner").map((r) => ({
72
- value: r,
73
- label: t(WORKSPACES_I18N_KEYS[ROLE_KEY[r]]),
74
- })) })] })] }) })] }));
75
- } }));
69
+ ...(canManage
70
+ ? [
71
+ {
72
+ title: "",
73
+ key: "actions",
74
+ render: (_, member) => (_jsx(Popconfirm, { title: t(WORKSPACES_I18N_KEYS.membersRemoveConfirm), onConfirm: () => remove(member.user_id), children: _jsx(Button, { danger: true, type: "link", children: t(WORKSPACES_I18N_KEYS.membersRemove) }) })),
75
+ },
76
+ ]
77
+ : []),
78
+ ];
79
+ return (_jsxs(Card, { "data-testid": "members-manager", children: [_jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 16 }, children: [_jsxs("div", { children: [_jsx(Typography.Title, { level: 4, style: { marginTop: 0 }, children: t(WORKSPACES_I18N_KEYS.membersTitle) }), _jsx(Typography.Text, { type: "secondary", children: t(WORKSPACES_I18N_KEYS.membersSubtitle) })] }), canManage && (_jsx(Button, { type: "primary", onClick: openInvite, "data-analytics": "none", "data-analytics-reason": "local-ui-open-invite-dialog", children: t(WORKSPACES_I18N_KEYS.membersInvite) }))] }), isError && error && (_jsx(Alert, { style: { marginTop: 12 }, type: "error", showIcon: true, message: error.message })), _jsx(Table, { style: { marginTop: 16 }, size: "small", loading: isLoading, rowKey: (member) => member.id, dataSource: members, columns: columns, pagination: false }), _jsx(Modal, { title: t(WORKSPACES_I18N_KEYS.membersInviteDialogTitle), open: inviteOpen, onCancel: () => setInviteOpen(false), onOk: submitInvite, okButtonProps: { loading: isInviting, disabled: emailsText.trim().length === 0 }, children: _jsxs("div", { style: { display: "grid", gap: 12 }, children: [_jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.membersInviteEmailsLabel) }), _jsx(Input, { value: emailsText, onChange: (e) => setEmailsText(e.target.value), placeholder: t(WORKSPACES_I18N_KEYS.membersInviteEmailsPlaceholder) })] }), _jsxs("div", { children: [_jsx(Typography.Text, { children: t(WORKSPACES_I18N_KEYS.membersInviteRoleLabel) }), _jsx(Select, { value: inviteRole, onChange: setInviteRole, style: { width: "100%" }, loading: rolesBag.isLoading, options: inviteRoleOptions })] })] }) })] }));
80
+ } })) }));
76
81
  }
77
82
  //# sourceMappingURL=MembersManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MembersManager.js","sourceRoot":"","sources":["../../src/default/MembersManager.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MembersManager.js","sourceRoot":"","sources":["../../src/default/MembersManager.tsx"],"names":[],"mappings":"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"}
@@ -15,4 +15,6 @@ export { WorkspaceSettings } from "./WorkspaceSettings.js";
15
15
  export type { WorkspaceSettingsProps } from "./WorkspaceSettings.js";
16
16
  export { MembersManager } from "./MembersManager.js";
17
17
  export type { MembersManagerProps } from "./MembersManager.js";
18
+ export { InviteAcceptPage } from "./InviteAcceptPage.js";
19
+ export type { InviteAcceptPageProps } from "./InviteAcceptPage.js";
18
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -13,4 +13,5 @@
13
13
  */
14
14
  export { WorkspaceSettings } from "./WorkspaceSettings.js";
15
15
  export { MembersManager } from "./MembersManager.js";
16
+ export { InviteAcceptPage } from "./InviteAcceptPage.js";
16
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,155 @@
1
+ import type { Analytics } from "@stapel/core";
2
+ import type { FlowMachine } from "@stapel/core";
3
+ import type { WorkspacesApi } from "../api/workspacesApi.js";
4
+ import type { InvitationPreview, Member } from "../api/types.js";
5
+ import type { FlowError } from "./errors.js";
6
+ /**
7
+ * The `/invite/{token}` journey (org-program §B4) as a flow machine:
8
+ *
9
+ * ```
10
+ * preview → (session && email match) → acceptPrompt [accept/decline]
11
+ * → (session && email mismatch) → wrongAccount (logout CTA)
12
+ * → (no session, email_registered)→ loginRequired (host's login slot)
13
+ * → (no session, new user) → newUser → claim →
14
+ * grantIssued (grant_token handed OUT) → [host exchanges at auth]
15
+ * → basicData (host's initial-setup slot) → acceptPrompt
16
+ * ```
17
+ *
18
+ * Non-`pending` invitations (expired / revoked / accepted / declined) park in
19
+ * the terminal `unavailable` step — the page renders the matching copy.
20
+ *
21
+ * ## The grant_token seam (deliberate pair boundary)
22
+ *
23
+ * `claim` mints a login grant that must be exchanged at AUTH's
24
+ * `POST /grant/exchange/` — an endpoint of the auth pair, and pairs do NOT
25
+ * depend on each other (no auth-react peer dependency here). The flow
26
+ * therefore parks in `grantIssued` and hands `grantToken` OUT via the
27
+ * `onLoginGrant` callback; the HOST connects the two pairs:
28
+ *
29
+ * ```tsx
30
+ * <InviteAcceptFlow token={token}
31
+ * onLoginGrant={async (grant) => {
32
+ * const r = await authApi.exchangeLoginGrant(grant); // auth-react
33
+ * session.adopt(r); // full session
34
+ * }}>
35
+ * ```
36
+ *
37
+ * then calls `grantExchanged()` (the default `InviteAcceptPage` does this
38
+ * automatically when the host's `onLoginGrant` promise resolves), which moves
39
+ * the flow to `basicData` — the host's profiles-react `InitialSetupPrompt`
40
+ * slot — and `completeBasicData()` finally reaches `acceptPrompt`. The
41
+ * account created by the exchange IS the invited email's account (verified by
42
+ * construction), so no re-match happens on that path.
43
+ *
44
+ * ## Session/email detection
45
+ *
46
+ * The pair has no auth dependency, so the CALLER supplies the signed-in
47
+ * email (`sessionEmail`, e.g. auth-react's `useAuthSessionState().user?.email`
48
+ * — or any other session source; null/undefined = no session). Match against
49
+ * the preview's MASKED email is decided client-side by masking the known
50
+ * email with the same algorithm (see `model/emailMask.ts`); the backend's
51
+ * real email-match on accept/decline stays the enforcement point — a mask
52
+ * collision merely shows the prompt, the accept then surfaces the 403.
53
+ */
54
+ export type InviteAcceptState = {
55
+ readonly step: "idle";
56
+ } | {
57
+ readonly step: "loadingPreview";
58
+ } | {
59
+ readonly step: "previewError";
60
+ readonly error: FlowError;
61
+ }
62
+ /** Terminal: the invitation is not pending (expired/revoked/accepted/declined). */
63
+ | {
64
+ readonly step: "unavailable";
65
+ readonly preview: InvitationPreview;
66
+ readonly status: string;
67
+ }
68
+ /** Signed in with a non-matching account — logout CTA (host slot). */
69
+ | {
70
+ readonly step: "wrongAccount";
71
+ readonly preview: InvitationPreview;
72
+ }
73
+ /** No session, account exists — the host's login slot; call
74
+ * `sessionEstablished(email)` once signed in. */
75
+ | {
76
+ readonly step: "loginRequired";
77
+ readonly preview: InvitationPreview;
78
+ }
79
+ /** No session, no account — deliberate CTA before minting the grant. */
80
+ | {
81
+ readonly step: "newUser";
82
+ readonly preview: InvitationPreview;
83
+ } | {
84
+ readonly step: "claiming";
85
+ readonly preview: InvitationPreview;
86
+ } | {
87
+ readonly step: "claimError";
88
+ readonly preview: InvitationPreview;
89
+ readonly error: FlowError;
90
+ }
91
+ /** The grant is out via `onLoginGrant` — waiting for the host to exchange
92
+ * it at auth and call `grantExchanged()`. */
93
+ | {
94
+ readonly step: "grantIssued";
95
+ readonly preview: InvitationPreview;
96
+ readonly grantToken: string;
97
+ }
98
+ /** Host's basic-data slot (profiles-react InitialSetupPrompt canon, §B5). */
99
+ | {
100
+ readonly step: "basicData";
101
+ readonly preview: InvitationPreview;
102
+ } | {
103
+ readonly step: "acceptPrompt";
104
+ readonly preview: InvitationPreview;
105
+ } | {
106
+ readonly step: "accepting";
107
+ readonly preview: InvitationPreview;
108
+ } | {
109
+ readonly step: "declining";
110
+ readonly preview: InvitationPreview;
111
+ } | {
112
+ readonly step: "acceptError";
113
+ readonly preview: InvitationPreview;
114
+ readonly error: FlowError;
115
+ } | {
116
+ readonly step: "accepted";
117
+ readonly member: Member;
118
+ readonly preview: InvitationPreview;
119
+ } | {
120
+ readonly step: "declined";
121
+ readonly preview: InvitationPreview;
122
+ };
123
+ export interface InviteAcceptFlow {
124
+ readonly machine: FlowMachine<InviteAcceptState>;
125
+ /** Fetch the preview and route per §B4. `sessionEmail` = the signed-in
126
+ * account's email, or null when there is no session. */
127
+ load(sessionEmail: string | null): Promise<void>;
128
+ /** From `newUser`/`claimError`: mint the login grant (`onLoginGrant` fires
129
+ * with the token; the flow parks in `grantIssued`). */
130
+ claim(): Promise<void>;
131
+ /** From `loginRequired`/`wrongAccount`: a session now exists (host's login
132
+ * slot finished, or the user switched accounts) — re-route on its email. */
133
+ sessionEstablished(email: string): void;
134
+ /** From `grantIssued`: the host exchanged the grant at auth — proceed to
135
+ * the basic-data step. */
136
+ grantExchanged(): void;
137
+ /** From `basicData`: the initial-setup slot finished — show the accept prompt. */
138
+ completeBasicData(): void;
139
+ accept(): Promise<void>;
140
+ decline(): Promise<void>;
141
+ reset(): void;
142
+ }
143
+ export interface InviteAcceptFlowDeps {
144
+ readonly api: WorkspacesApi;
145
+ /** The invite token from the `/invite/{token}` URL — a bearer secret. */
146
+ readonly token: string;
147
+ readonly analytics?: Analytics | null;
148
+ /** The grant_token hand-off seam (see the module doc). A credential:
149
+ * exchange it, never log it. */
150
+ readonly onLoginGrant?: (grantToken: string) => void;
151
+ readonly onAccepted?: (member: Member) => void;
152
+ readonly onDeclined?: () => void;
153
+ }
154
+ export declare function createInviteAcceptFlow(deps: InviteAcceptFlowDeps): InviteAcceptFlow;
155
+ //# sourceMappingURL=inviteAcceptFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inviteAcceptFlow.d.ts","sourceRoot":"","sources":["../../src/flows/inviteAcceptFlow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ import { createFlowMachine } from "@stapel/core";
2
+ import { emailMatchesMask } from "../model/emailMask.js";
3
+ import { toFlowError } from "./errors.js";
4
+ /**
5
+ * Flow id: stapel-workspaces annotates no `@flow_step` yet (its
6
+ * `docs/flows.json` is empty — the pair's flow registry is the zero-flow
7
+ * shim), so this id is pair-local until the backend documents the flow;
8
+ * switch to the generated registry id when it lands.
9
+ */
10
+ const FLOW_ID = "workspaces.invite_accept";
11
+ function routeSignedIn(preview, email) {
12
+ return emailMatchesMask(email, preview.email_masked)
13
+ ? { step: "acceptPrompt", preview }
14
+ : { step: "wrongAccount", preview };
15
+ }
16
+ function routePreview(preview, sessionEmail) {
17
+ if (preview.status !== "pending") {
18
+ return { step: "unavailable", preview, status: preview.status };
19
+ }
20
+ if (sessionEmail !== null && sessionEmail !== "") {
21
+ return routeSignedIn(preview, sessionEmail);
22
+ }
23
+ return preview.email_registered
24
+ ? { step: "loginRequired", preview }
25
+ : { step: "newUser", preview };
26
+ }
27
+ export function createInviteAcceptFlow(deps) {
28
+ const machine = createFlowMachine({
29
+ id: FLOW_ID,
30
+ initial: { step: "idle" },
31
+ analytics: deps.analytics ?? null,
32
+ });
33
+ async function load(sessionEmail) {
34
+ await machine.run({ step: "loadingPreview" }, () => deps.api.getInvitationPreview(deps.token), {
35
+ resolve: (preview) => routePreview(preview, sessionEmail),
36
+ reject: (error) => ({
37
+ step: "previewError",
38
+ error: toFlowError(error),
39
+ }),
40
+ });
41
+ }
42
+ async function claim() {
43
+ const s = machine.getState();
44
+ if (s.step !== "newUser" && s.step !== "claimError")
45
+ return;
46
+ const { preview } = s;
47
+ await machine.run({ step: "claiming", preview }, () => deps.api.claimInvitation(deps.token), {
48
+ resolve: (r) => {
49
+ deps.onLoginGrant?.(r.grant_token);
50
+ return { step: "grantIssued", preview, grantToken: r.grant_token };
51
+ },
52
+ reject: (error) => ({
53
+ step: "claimError",
54
+ preview,
55
+ error: toFlowError(error),
56
+ }),
57
+ });
58
+ }
59
+ function sessionEstablished(email) {
60
+ const s = machine.getState();
61
+ if (s.step !== "loginRequired" && s.step !== "wrongAccount")
62
+ return;
63
+ machine.to(routeSignedIn(s.preview, email));
64
+ }
65
+ function grantExchanged() {
66
+ const s = machine.getState();
67
+ if (s.step !== "grantIssued")
68
+ return;
69
+ machine.to({ step: "basicData", preview: s.preview });
70
+ }
71
+ function completeBasicData() {
72
+ const s = machine.getState();
73
+ if (s.step !== "basicData")
74
+ return;
75
+ machine.to({ step: "acceptPrompt", preview: s.preview });
76
+ }
77
+ async function accept() {
78
+ const s = machine.getState();
79
+ if (s.step !== "acceptPrompt" && s.step !== "acceptError")
80
+ return;
81
+ const { preview } = s;
82
+ await machine.run({ step: "accepting", preview }, () => deps.api.acceptInvitation({ token: deps.token }), {
83
+ resolve: (member) => {
84
+ deps.onAccepted?.(member);
85
+ return { step: "accepted", member, preview };
86
+ },
87
+ reject: (error) => ({
88
+ step: "acceptError",
89
+ preview,
90
+ error: toFlowError(error),
91
+ }),
92
+ });
93
+ }
94
+ async function decline() {
95
+ const s = machine.getState();
96
+ if (s.step !== "acceptPrompt" && s.step !== "acceptError")
97
+ return;
98
+ const { preview } = s;
99
+ await machine.run({ step: "declining", preview }, () => deps.api.declineInvitation(deps.token), {
100
+ resolve: () => {
101
+ deps.onDeclined?.();
102
+ return { step: "declined", preview };
103
+ },
104
+ reject: (error) => ({
105
+ step: "acceptError",
106
+ preview,
107
+ error: toFlowError(error),
108
+ }),
109
+ });
110
+ }
111
+ function reset() {
112
+ machine.to({ step: "idle" });
113
+ }
114
+ return {
115
+ machine,
116
+ load,
117
+ claim,
118
+ sessionEstablished,
119
+ grantExchanged,
120
+ completeBasicData,
121
+ accept,
122
+ decline,
123
+ reset,
124
+ };
125
+ }
126
+ //# sourceMappingURL=inviteAcceptFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inviteAcceptFlow.js","sourceRoot":"","sources":["../../src/flows/inviteAcceptFlow.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAIjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAmI1C;;;;;GAKG;AACH,MAAM,OAAO,GAAG,0BAA0B,CAAC;AAE3C,SAAS,aAAa,CACpB,OAA0B,EAC1B,KAAa;IAEb,OAAO,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC;QAClD,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE;QACnC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;AACxC,CAAC;AAED,SAAS,YAAY,CACnB,OAA0B,EAC1B,YAA2B;IAE3B,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAClE,CAAC;IACD,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,KAAK,EAAE,EAAE,CAAC;QACjD,OAAO,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,OAAO,CAAC,gBAAgB;QAC7B,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE;QACpC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,IAA0B;IAE1B,MAAM,OAAO,GAAG,iBAAiB,CAAoB;QACnD,EAAE,EAAE,OAAO;QACX,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;KAClC,CAAC,CAAC;IAEH,KAAK,UAAU,IAAI,CAAC,YAA2B;QAC7C,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAC1B,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,EAC/C;YACE,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC;YACzD,MAAM,EAAE,CAAC,KAAK,EAAqB,EAAE,CAAC,CAAC;gBACrC,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,KAAK,UAAU,KAAK;QAClB,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO;QAC5D,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QACtB,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,EAC7B,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAC1C;YACE,OAAO,EAAE,CAAC,CAAC,EAAqB,EAAE;gBAChC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;gBACnC,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YACrE,CAAC;YACD,MAAM,EAAE,CAAC,KAAK,EAAqB,EAAE,CAAC,CAAC;gBACrC,IAAI,EAAE,YAAY;gBAClB,OAAO;gBACP,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,SAAS,kBAAkB,CAAC,KAAa;QACvC,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,eAAe,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc;YAAE,OAAO;QACpE,OAAO,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,SAAS,cAAc;QACrB,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa;YAAE,OAAO;QACrC,OAAO,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,SAAS,iBAAiB;QACxB,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW;YAAE,OAAO;QACnC,OAAO,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,UAAU,MAAM;QACnB,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa;YAAE,OAAO;QAClE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QACtB,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAC9B,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,EACtD;YACE,OAAO,EAAE,CAAC,MAAM,EAAqB,EAAE;gBACrC,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC;gBAC1B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAC/C,CAAC;YACD,MAAM,EAAE,CAAC,KAAK,EAAqB,EAAE,CAAC,CAAC;gBACrC,IAAI,EAAE,aAAa;gBACnB,OAAO;gBACP,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,KAAK,UAAU,OAAO;QACpB,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa;YAAE,OAAO;QAClE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QACtB,MAAM,OAAO,CAAC,GAAG,CACf,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAC9B,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAC5C;YACE,OAAO,EAAE,GAAsB,EAAE;gBAC/B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;gBACpB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;YACvC,CAAC;YACD,MAAM,EAAE,CAAC,KAAK,EAAqB,EAAE,CAAC,CAAC;gBACrC,IAAI,EAAE,aAAa;gBACnB,OAAO;gBACP,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;aAC1B,CAAC;SACH,CACF,CAAC;IACJ,CAAC;IAED,SAAS,KAAK;QACZ,OAAO,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO;QACL,OAAO;QACP,IAAI;QACJ,KAAK;QACL,kBAAkB;QAClB,cAAc;QACd,iBAAiB;QACjB,MAAM;QACN,OAAO;QACP,KAAK;KACN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { ReactNode } from "react";
2
+ /** Render-prop bag for {@link Can}'s function-children variant. */
3
+ export interface CanBag {
4
+ /** The wildcard-aware verdict for `capability` in `workspaceId`. */
5
+ readonly allowed: boolean;
6
+ /** The underlying read is still in flight (verdict is `false` meanwhile —
7
+ * deny-by-default; render a skeleton off this if flicker matters). */
8
+ readonly isLoading: boolean;
9
+ }
10
+ /**
11
+ * Capability gate (org-program §A2) — the mandate model's UI affordance:
12
+ *
13
+ * ```tsx
14
+ * <Can capability="meetings.spotlight" workspaceId={wsId}>
15
+ * <SpotlightButton />
16
+ * </Can>
17
+ * // or the render-prop variant, when "disabled" beats "hidden":
18
+ * <Can capability="members.invite" workspaceId={wsId}>
19
+ * {({ allowed }) => <Button disabled={!allowed}>…</Button>}
20
+ * </Can>
21
+ * ```
22
+ *
23
+ * Reads the caller's `my_capabilities` from the workspace detail
24
+ * ({@link useCapabilities}) and matches with the backend's ported wildcard
25
+ * matcher (`*` / `prefix.*`). Deny-by-default: nothing renders (or
26
+ * `allowed: false`) while loading, on error, for a non-member, or against a
27
+ * pre-0.6 backend. UI convenience ONLY — the backend re-checks every
28
+ * capability on every operation; hiding a button is not access control.
29
+ */
30
+ export declare function Can(props: {
31
+ /** Namespaced capability string, e.g. `"meetings.spotlight"`. */
32
+ capability: string;
33
+ workspaceId: string;
34
+ /** Static children render when allowed; function children always render,
35
+ * receiving the {@link CanBag}. */
36
+ children: ReactNode | ((bag: CanBag) => ReactNode);
37
+ /** Rendered when the verdict is a definite "no" (static children only). */
38
+ fallback?: ReactNode;
39
+ }): ReactNode;
40
+ //# sourceMappingURL=Can.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Can.d.ts","sourceRoot":"","sources":["../../src/headless/Can.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,mEAAmE;AACnE,MAAM,WAAW,MAAM;IACrB,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;0EACsE;IACtE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE;IACzB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;uCACmC;IACnC,QAAQ,EAAE,SAAS,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC;IACnD,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,GAAG,SAAS,CAOZ"}
@@ -0,0 +1,30 @@
1
+ import { useCapabilities } from "../model/queries.js";
2
+ /**
3
+ * Capability gate (org-program §A2) — the mandate model's UI affordance:
4
+ *
5
+ * ```tsx
6
+ * <Can capability="meetings.spotlight" workspaceId={wsId}>
7
+ * <SpotlightButton />
8
+ * </Can>
9
+ * // or the render-prop variant, when "disabled" beats "hidden":
10
+ * <Can capability="members.invite" workspaceId={wsId}>
11
+ * {({ allowed }) => <Button disabled={!allowed}>…</Button>}
12
+ * </Can>
13
+ * ```
14
+ *
15
+ * Reads the caller's `my_capabilities` from the workspace detail
16
+ * ({@link useCapabilities}) and matches with the backend's ported wildcard
17
+ * matcher (`*` / `prefix.*`). Deny-by-default: nothing renders (or
18
+ * `allowed: false`) while loading, on error, for a non-member, or against a
19
+ * pre-0.6 backend. UI convenience ONLY — the backend re-checks every
20
+ * capability on every operation; hiding a button is not access control.
21
+ */
22
+ export function Can(props) {
23
+ const caps = useCapabilities(props.workspaceId);
24
+ const allowed = caps.can(props.capability);
25
+ if (typeof props.children === "function") {
26
+ return props.children({ allowed, isLoading: caps.isLoading });
27
+ }
28
+ return allowed ? props.children : (props.fallback ?? null);
29
+ }
30
+ //# sourceMappingURL=Can.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Can.js","sourceRoot":"","sources":["../../src/headless/Can.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAWtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,GAAG,CAAC,KASnB;IACC,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { ReactNode } from "react";
2
+ import type { InviteAcceptState } from "../flows/inviteAcceptFlow.js";
3
+ import type { Member } from "../api/types.js";
4
+ /** Render-prop bag for {@link InviteAcceptFlow}. */
5
+ export interface InviteAcceptFlowBag {
6
+ readonly state: InviteAcceptState;
7
+ /** From `newUser`/`claimError`: mint the login grant. */
8
+ claim(): void;
9
+ /** From `loginRequired`/`wrongAccount`: a session now exists — re-route on
10
+ * its email. (Also fired automatically when the `sessionEmail` prop
11
+ * becomes non-null while parked in either step.) */
12
+ sessionEstablished(email: string): void;
13
+ /** From `grantIssued`: the host exchanged the grant at auth. */
14
+ grantExchanged(): void;
15
+ /** From `basicData`: the initial-setup slot finished. */
16
+ completeBasicData(): void;
17
+ accept(): void;
18
+ decline(): void;
19
+ /** Re-fetch the preview and re-route (e.g. after a logout on wrongAccount). */
20
+ reload(): void;
21
+ }
22
+ /**
23
+ * Headless `/invite/{token}` journey (org-program §B4) — the flow machine
24
+ * (`flows/inviteAcceptFlow.ts`, see ITS doc for the full state graph and the
25
+ * grant_token hand-off seam) wired to this pair's runtime. Renderless: every
26
+ * screen (accept modal, wrong-account, the login and initial-setup SLOTS) is
27
+ * the host's; the default `InviteAcceptPage` is the reference skin.
28
+ *
29
+ * The pair knows nothing about auth: pass the signed-in email (or null) as
30
+ * `sessionEmail`, and connect `onLoginGrant` to auth-react's
31
+ * `exchangeLoginGrant` yourself — then call `grantExchanged()`.
32
+ */
33
+ export declare function InviteAcceptFlow(props: {
34
+ /** The invite token from the URL — a bearer secret, never log it. */
35
+ token: string;
36
+ /** The signed-in account's email, or null/undefined when no session. */
37
+ sessionEmail?: string | null;
38
+ children: (bag: InviteAcceptFlowBag) => ReactNode;
39
+ /** The grant_token hand-off (a credential — exchange, never log). */
40
+ onLoginGrant?: (grantToken: string) => void;
41
+ onAccepted?: (member: Member) => void;
42
+ onDeclined?: () => void;
43
+ }): ReactNode;
44
+ //# sourceMappingURL=InviteAcceptFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InviteAcceptFlow.d.ts","sourceRoot":"","sources":["../../src/headless/InviteAcceptFlow.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAG9C,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,yDAAyD;IACzD,KAAK,IAAI,IAAI,CAAC;IACd;;wDAEoD;IACpD,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,gEAAgE;IAChE,cAAc,IAAI,IAAI,CAAC;IACvB,yDAAyD;IACzD,iBAAiB,IAAI,IAAI,CAAC;IAC1B,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAChB,+EAA+E;IAC/E,MAAM,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,SAAS,CAAC;IAClD,qEAAqE;IACrE,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,GAAG,SAAS,CAwDZ"}