@cosmoops/connect-sdk 0.9.0 → 0.16.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 (155) hide show
  1. package/CHANGELOG.md +566 -0
  2. package/README.md +113 -42
  3. package/dist/cjs/cli/doctor.js +3 -3
  4. package/dist/cjs/cli/init.js +3 -3
  5. package/dist/cjs/client/components/access-gate.js +20 -1
  6. package/dist/cjs/client/components/ai-screens.js +73 -0
  7. package/dist/cjs/client/components/ai-widgets.js +37 -0
  8. package/dist/cjs/client/components/app-shell.js +75 -41
  9. package/dist/cjs/client/components/apps-grid.js +3 -2
  10. package/dist/cjs/client/components/ask-ai.js +50 -5
  11. package/dist/cjs/client/components/confirm-dialog.js +40 -0
  12. package/dist/cjs/client/components/cursor-pagination.js +22 -0
  13. package/dist/cjs/client/components/filter-tabs.js +24 -0
  14. package/dist/cjs/client/components/grievance-notice.js +30 -0
  15. package/dist/cjs/client/components/link-button.js +17 -10
  16. package/dist/cjs/client/components/logo.js +4 -3
  17. package/dist/cjs/client/components/nav.js +41 -0
  18. package/dist/cjs/client/components/plan-pricing.js +27 -0
  19. package/dist/cjs/client/components/plans.js +1 -1
  20. package/dist/cjs/client/components/platform-gate.js +48 -0
  21. package/dist/cjs/client/components/shell-context.js +149 -0
  22. package/dist/cjs/client/components/side-nav.js +49 -23
  23. package/dist/cjs/client/components/sidebar.js +127 -0
  24. package/dist/cjs/client/components/subscribe-dialog.js +2 -2
  25. package/dist/cjs/client/components/subscription.js +1 -1
  26. package/dist/cjs/client/components/user-menu.js +96 -37
  27. package/dist/cjs/client/components/workspace-switcher.js +100 -0
  28. package/dist/cjs/client/index.js +72 -2
  29. package/dist/cjs/client/provider.js +57 -2
  30. package/dist/cjs/client/use-checkout.js +9 -5
  31. package/dist/cjs/errors.js +1 -1
  32. package/dist/cjs/index.js +16 -1
  33. package/dist/cjs/mcp/index.js +90 -0
  34. package/dist/cjs/mcp/tools.js +369 -0
  35. package/dist/cjs/next/index.js +2 -2
  36. package/dist/cjs/server/client.js +25 -2
  37. package/dist/cjs/server/handler.js +203 -18
  38. package/dist/cjs/server/http.js +6 -2
  39. package/dist/cjs/server/resources/acting-as.js +16 -0
  40. package/dist/cjs/server/resources/ai.js +22 -2
  41. package/dist/cjs/server/resources/data.js +374 -0
  42. package/dist/cjs/server/resources/identity.js +7 -3
  43. package/dist/cjs/server/resources/kv.js +84 -0
  44. package/dist/cjs/server/resources/platform.js +43 -0
  45. package/dist/cjs/server/resources/sessions.js +1 -1
  46. package/dist/cjs/server/resources/workspaces.js +74 -0
  47. package/dist/cjs/store-schema.js +66 -0
  48. package/dist/cjs/store.js +152 -0
  49. package/dist/cjs/ui/empty-state.js +28 -0
  50. package/dist/cjs/ui/index.js +44 -0
  51. package/dist/cjs/ui/page-header.js +27 -0
  52. package/dist/cjs/ui/page-shell.js +44 -0
  53. package/dist/cjs/ui/section.js +20 -0
  54. package/dist/cjs/ui/setting-row.js +14 -0
  55. package/dist/cjs/ui/status-chip.js +17 -0
  56. package/dist/esm/cli/doctor.js +3 -3
  57. package/dist/esm/cli/init.js +3 -3
  58. package/dist/esm/client/components/access-gate.js +20 -1
  59. package/dist/esm/client/components/ai-screens.js +69 -0
  60. package/dist/esm/client/components/ai-widgets.js +37 -1
  61. package/dist/esm/client/components/app-shell.js +81 -47
  62. package/dist/esm/client/components/apps-grid.js +3 -2
  63. package/dist/esm/client/components/ask-ai.js +51 -6
  64. package/dist/esm/client/components/confirm-dialog.js +37 -0
  65. package/dist/esm/client/components/cursor-pagination.js +19 -0
  66. package/dist/esm/client/components/filter-tabs.js +21 -0
  67. package/dist/esm/client/components/grievance-notice.js +27 -0
  68. package/dist/esm/client/components/link-button.js +17 -10
  69. package/dist/esm/client/components/logo.js +4 -3
  70. package/dist/esm/client/components/nav.js +40 -0
  71. package/dist/esm/client/components/plan-pricing.js +25 -0
  72. package/dist/esm/client/components/plans.js +2 -2
  73. package/dist/esm/client/components/platform-gate.js +45 -0
  74. package/dist/esm/client/components/shell-context.js +141 -0
  75. package/dist/esm/client/components/side-nav.js +48 -21
  76. package/dist/esm/client/components/sidebar.js +120 -0
  77. package/dist/esm/client/components/subscribe-dialog.js +3 -3
  78. package/dist/esm/client/components/subscription.js +2 -2
  79. package/dist/esm/client/components/user-menu.js +95 -38
  80. package/dist/esm/client/components/workspace-switcher.js +97 -0
  81. package/dist/esm/client/index.js +47 -4
  82. package/dist/esm/client/provider.js +53 -2
  83. package/dist/esm/client/use-checkout.js +9 -5
  84. package/dist/esm/errors.js +1 -1
  85. package/dist/esm/index.js +4 -0
  86. package/dist/esm/mcp/index.js +85 -0
  87. package/dist/esm/mcp/tools.js +366 -0
  88. package/dist/esm/next/index.js +2 -2
  89. package/dist/esm/server/client.js +25 -2
  90. package/dist/esm/server/handler.js +203 -18
  91. package/dist/esm/server/http.js +6 -2
  92. package/dist/esm/server/resources/acting-as.js +13 -0
  93. package/dist/esm/server/resources/ai.js +22 -2
  94. package/dist/esm/server/resources/data.js +369 -0
  95. package/dist/esm/server/resources/identity.js +7 -3
  96. package/dist/esm/server/resources/kv.js +80 -0
  97. package/dist/esm/server/resources/platform.js +39 -0
  98. package/dist/esm/server/resources/sessions.js +1 -1
  99. package/dist/esm/server/resources/workspaces.js +70 -0
  100. package/dist/esm/store-schema.js +62 -0
  101. package/dist/esm/store.js +141 -0
  102. package/dist/esm/ui/empty-state.js +25 -0
  103. package/dist/esm/ui/index.js +34 -0
  104. package/dist/esm/ui/page-header.js +24 -0
  105. package/dist/esm/ui/page-shell.js +41 -0
  106. package/dist/esm/ui/section.js +17 -0
  107. package/dist/esm/ui/setting-row.js +11 -0
  108. package/dist/esm/ui/status-chip.js +14 -0
  109. package/dist/types/client/components/ai-screens.d.ts +25 -0
  110. package/dist/types/client/components/ai-widgets.d.ts +34 -1
  111. package/dist/types/client/components/app-shell.d.ts +65 -27
  112. package/dist/types/client/components/apps-grid.d.ts +11 -2
  113. package/dist/types/client/components/ask-ai.d.ts +47 -1
  114. package/dist/types/client/components/confirm-dialog.d.ts +34 -0
  115. package/dist/types/client/components/cursor-pagination.d.ts +27 -0
  116. package/dist/types/client/components/filter-tabs.d.ts +26 -0
  117. package/dist/types/client/components/grievance-notice.d.ts +31 -0
  118. package/dist/types/client/components/link-button.d.ts +32 -9
  119. package/dist/types/client/components/logo.d.ts +18 -2
  120. package/dist/types/client/components/nav.d.ts +24 -0
  121. package/dist/types/client/components/plan-pricing.d.ts +10 -0
  122. package/dist/types/client/components/platform-gate.d.ts +53 -0
  123. package/dist/types/client/components/shell-context.d.ts +116 -0
  124. package/dist/types/client/components/side-nav.d.ts +27 -13
  125. package/dist/types/client/components/sidebar.d.ts +95 -0
  126. package/dist/types/client/components/user-menu.d.ts +68 -22
  127. package/dist/types/client/components/workspace-switcher.d.ts +49 -0
  128. package/dist/types/client/index.d.ts +29 -5
  129. package/dist/types/client/provider.d.ts +109 -6
  130. package/dist/types/client/use-checkout.d.ts +8 -4
  131. package/dist/types/errors.d.ts +1 -1
  132. package/dist/types/index.d.ts +6 -0
  133. package/dist/types/mcp/index.d.ts +74 -0
  134. package/dist/types/mcp/tools.d.ts +107 -0
  135. package/dist/types/next/index.d.ts +1 -1
  136. package/dist/types/server/client.d.ts +25 -2
  137. package/dist/types/server/http.d.ts +8 -2
  138. package/dist/types/server/index.d.ts +4 -0
  139. package/dist/types/server/resources/acting-as.d.ts +11 -0
  140. package/dist/types/server/resources/ai.d.ts +20 -2
  141. package/dist/types/server/resources/data.d.ts +258 -0
  142. package/dist/types/server/resources/kv.d.ts +63 -0
  143. package/dist/types/server/resources/platform.d.ts +32 -0
  144. package/dist/types/server/resources/workspaces.d.ts +55 -0
  145. package/dist/types/store-schema.d.ts +52 -0
  146. package/dist/types/store.d.ts +96 -0
  147. package/dist/types/types.d.ts +369 -16
  148. package/dist/types/ui/empty-state.d.ts +34 -0
  149. package/dist/types/ui/index.d.ts +40 -0
  150. package/dist/types/ui/page-header.d.ts +34 -0
  151. package/dist/types/ui/page-shell.d.ts +39 -0
  152. package/dist/types/ui/section.d.ts +26 -0
  153. package/dist/types/ui/setting-row.d.ts +18 -0
  154. package/dist/types/ui/status-chip.d.ts +36 -0
  155. package/package.json +15 -2
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ConnectProfileModal = ConnectProfileModal;
5
+ exports.ConnectUserButton = ConnectUserButton;
4
6
  exports.ConnectUserMenu = ConnectUserMenu;
5
7
  exports.ConnectManageSubscriptionButton = ConnectManageSubscriptionButton;
6
8
  exports.ConnectInvoicesButton = ConnectInvoicesButton;
@@ -13,6 +15,8 @@ const lucide_react_1 = require("lucide-react");
13
15
  const provider_js_1 = require("../provider.js");
14
16
  const mark_js_1 = require("../mark.js");
15
17
  const account_sessions_js_1 = require("./account-sessions.js");
18
+ const motion_js_1 = require("./motion.js");
19
+ const shell_context_js_1 = require("./shell-context.js");
16
20
  const surfaces_js_1 = require("./surfaces.js");
17
21
  const THEME_OPTIONS = [
18
22
  { value: "light", label: "Light", icon: lucide_react_1.Sun },
@@ -61,7 +65,15 @@ function profileUrl(appUrl, clientId) {
61
65
  * had set on their profile.
62
66
  */
63
67
  function UserAvatar({ user, size, }) {
64
- return ((0, jsx_runtime_1.jsxs)(react_1.Avatar, { size: size, color: "accent", variant: "soft", children: [user.photoUrl && (0, jsx_runtime_1.jsx)(react_1.Avatar.Image, { alt: "", src: user.photoUrl }), (0, jsx_runtime_1.jsx)(react_1.Avatar.Fallback, { children: initials(user.displayName, user.email) })] }));
68
+ return (
69
+ // `rounded-full` is an override, not decoration. HeroUI's `.avatar` is a
70
+ // squircle - `rounded-2xl` at `sm`, `rounded-3xl` at `lg` - which is right
71
+ // for the media component in general and wrong for a *person*: every other
72
+ // product a customer uses draws an account avatar as a circle, and a
73
+ // squircle sitting beside circular status dots reads as a rendering fault
74
+ // rather than a choice. The root is `overflow-hidden`, so rounding it here
75
+ // rounds the image and the fallback with it.
76
+ (0, jsx_runtime_1.jsxs)(react_1.Avatar, { size: size, color: "accent", variant: "soft", className: "rounded-full", children: [user.photoUrl && (0, jsx_runtime_1.jsx)(react_1.Avatar.Image, { alt: "", src: user.photoUrl }), (0, jsx_runtime_1.jsx)(react_1.Avatar.Fallback, { children: initials(user.displayName, user.email) })] }));
65
77
  }
66
78
  /** `dispatch_admin` / `billing-viewer` -> `Dispatch admin`. */
67
79
  function humanizeRole(role) {
@@ -116,55 +128,102 @@ function DestinationTile({ icon, label, url, }) {
116
128
  return ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", "aria-label": `${label} (opens in a new tab)`, className: `relative h-auto w-full flex-col items-center justify-center gap-2 rounded-xl border border-border ${surfaces_js_1.PANEL_FILL} p-3 hover:border-accent hover:bg-surface-secondary`, onPress: () => openExternal(url), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "absolute end-2 top-2 size-3.5 text-muted", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { className: `flex ${surfaces_js_1.MEDALLION} items-center justify-center rounded-xl bg-surface text-muted`, children: icon }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium text-foreground", children: label })] }));
117
129
  }
118
130
  /**
119
- * Avatar and name in the trigger; an identity card, the three Connect
120
- * destinations (Profile, Billing, Connect), an optional appearance control,
121
- * this account's active sessions, and sign-out in the modal it opens.
131
+ * The account modal's contents - identity card, the three Connect
132
+ * destinations, an optional appearance control, this account's sessions, and
133
+ * sign-out.
134
+ *
135
+ * Extracted from the trigger it used to be welded to, so the same surface can
136
+ * be opened from the sidebar's account row (`ConnectUserButton`) *or* from a
137
+ * host's own control (`ConnectProfileModal`) without a second copy of it. It
138
+ * renders the `Modal.Backdrop` downwards only; whoever renders it supplies the
139
+ * `<Modal>` and decides how it opens.
122
140
  *
123
141
  * Profile/Billing/Connect open in a **new tab**, each marked with an
124
- * external-link icon. They are a different application at a different
125
- * origin, and navigating the host app away to reach them would lose
126
- * whatever the user was doing here. Sign out is the only action that acts
127
- * in place, which is why it sits alone in the footer rather than as a fourth
128
- * item in the same list as three navigations.
142
+ * external-link icon. They are a different application at a different origin,
143
+ * and navigating the host app away to reach them would lose whatever the user
144
+ * was doing here. Sign out is the only action that acts in place, which is why
145
+ * it sits alone in the footer rather than as a fourth item in the same list as
146
+ * three navigations.
147
+ */
148
+ function ProfileSurface({ onSignOut, theme, onThemeChange, }) {
149
+ const { session, signOut, appUrl, clientId } = (0, provider_js_1.useConnect)();
150
+ const user = session?.user;
151
+ if (!user)
152
+ return null;
153
+ const label = user.displayName ?? user.email ?? "Account";
154
+ const role = session?.role ?? null;
155
+ const planChip = subscriptionChip(session?.subscription ?? null);
156
+ return ((0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { size: "md", scroll: "inside", children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_1.Modal.CloseTrigger, {}), (0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Heading, { children: "Account" }) }), (0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "flex flex-col gap-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: `flex items-start gap-3 rounded-xl border border-border ${surfaces_js_1.PANEL_FILL} p-4`, children: [(0, jsx_runtime_1.jsx)(UserAvatar, { user: user, size: "lg" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm font-semibold text-foreground", children: label }), user.email && (0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: user.email }), (0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: user.workspaceName }), (role || planChip || !user.emailVerified) && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-1.5 flex flex-wrap items-center gap-1.5", children: [role && ((0, jsx_runtime_1.jsx)(react_1.Chip, { size: "sm", variant: "soft", color: "accent", children: humanizeRole(role) })), planChip, !user.emailVerified && ((0, jsx_runtime_1.jsx)(react_1.Chip, { size: "sm", variant: "soft", color: "warning", children: "Email unverified" }))] }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [(0, jsx_runtime_1.jsx)(DestinationTile, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.UserRound, { className: surfaces_js_1.ICON, "aria-hidden": "true" }), label: "Profile", url: profileUrl(appUrl, clientId) }), (0, jsx_runtime_1.jsx)(DestinationTile, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.CreditCard, { className: surfaces_js_1.ICON, "aria-hidden": "true" }), label: "Billing", url: `${appUrl}/billing` }), (0, jsx_runtime_1.jsx)(DestinationTile, { icon: (0, jsx_runtime_1.jsx)(mark_js_1.ConnectMark, { className: surfaces_js_1.ICON }), label: "Connect", url: appUrl })] }), theme && onThemeChange && ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-2", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Appearance" }), (0, jsx_runtime_1.jsx)(react_1.ToggleButtonGroup, { fullWidth: true, "aria-label": "Theme", selectionMode: "single", disallowEmptySelection: true, selectedKeys: [theme], onSelectionChange: (keys) => {
157
+ const [next] = [...keys];
158
+ if (typeof next === "string")
159
+ onThemeChange(next);
160
+ }, children: THEME_OPTIONS.map(({ value, label: optionLabel, icon: Icon }, index) => ((0, jsx_runtime_1.jsxs)(react_1.ToggleButton, { id: value, children: [index > 0 && (0, jsx_runtime_1.jsx)(react_1.ToggleButtonGroup.Separator, {}), (0, jsx_runtime_1.jsx)(Icon, { className: "size-4", "aria-hidden": "true" }), optionLabel] }, value))) })] })), (0, jsx_runtime_1.jsx)(account_sessions_js_1.ConnectAccountSessions, {})] }), (0, jsx_runtime_1.jsx)(react_1.Modal.Footer, { children: (0, jsx_runtime_1.jsxs)(react_1.Button, { slot: "close", fullWidth: true, variant: "tertiary", className: "text-danger", onPress: () => {
161
+ void signOut().then(onSignOut);
162
+ }, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.LogOut, { className: "size-4 shrink-0" }), "Sign out"] }) })] }) }) }));
163
+ }
164
+ /**
165
+ * The account modal with **no trigger of its own**, driven by the host.
166
+ *
167
+ * For an application that already has an account control it wants to keep -
168
+ * an avatar in its own chrome, a "Manage account" row in its own settings
169
+ * page - and wants Connect's modal behind it rather than rebuilding identity,
170
+ * sessions and sign-out. `ConnectUserButton` is the same modal with the
171
+ * trigger supplied; reach for this one only when that trigger is in the way.
129
172
  *
130
- * The session list is account-wide, not scoped to this application - see
131
- * `ConnectUserSession`'s doc comment in `types.ts`. Appearance only renders
132
- * when both `theme` and `onThemeChange` are supplied.
173
+ * Renders nothing when signed out, open or not - there is no account to show.
174
+ */
175
+ function ConnectProfileModal({ isOpen, onOpenChange, onSignOut, theme, onThemeChange, }) {
176
+ const { session } = (0, provider_js_1.useConnect)();
177
+ if (!session?.user)
178
+ return null;
179
+ return ((0, jsx_runtime_1.jsx)(react_1.Modal, { isOpen: isOpen, onOpenChange: onOpenChange, children: (0, jsx_runtime_1.jsx)(ProfileSurface, { onSignOut: onSignOut, theme: theme, onThemeChange: onThemeChange }) }));
180
+ }
181
+ /**
182
+ * The signed-in account, as a control that opens the account modal.
133
183
  *
134
- * Renders nothing when signed out - pair it with `<ConnectLoginButton>`, which
135
- * renders nothing when signed *in*, and the two swap cleanly with no branch in
136
- * your own layout.
184
+ * In `"row"` form this is the last thing in the sidebar, which is where an
185
+ * account control belongs: it is the one item in the rail that is about *you*
186
+ * rather than about the product, and the foot of a column is where every
187
+ * application a customer already uses puts that. It collapses with the rail -
188
+ * the avatar holds its position and the name fades - because the avatar alone
189
+ * is still a complete account control, which is exactly what an icon rail
190
+ * needs.
191
+ *
192
+ * Renders nothing when signed out. Pair it with `<ConnectLoginButton>`, which
193
+ * renders nothing when signed *in*, and the two swap with no branch in your
194
+ * own layout.
137
195
  *
138
196
  * On the HeroUI shape: the trigger is a plain `<Button>` as a **direct child**
139
- * of `<Modal>`, not wrapped in `<Modal.Trigger>`. `Dropdown.Trigger` (and by
140
- * the same construction, `Modal.Trigger`) is itself a React Aria `Button`, so
141
- * wrapping one produces a button inside a button - invalid markup, and press
142
- * handling stops working. Kept uncontrolled (no `isOpen`/`onOpenChange`),
143
- * matching `<ConnectAppsGrid>`'s own `<Modal>` - nothing here needs to close
144
- * the modal programmatically; sign-out uses `slot="close"` instead.
197
+ * of `<Modal>`, not wrapped in `<Modal.Trigger>`. `Dropdown.Trigger` - and by
198
+ * the same construction `Modal.Trigger` - is itself a React Aria `Button`, so
199
+ * wrapping one produces a button inside a button: invalid markup, and press
200
+ * handling stops working.
145
201
  */
146
- function ConnectUserMenu({ compact = false, onSignOut, className, theme, onThemeChange, }) {
147
- const { session, isLoading, signOut, appUrl, clientId } = (0, provider_js_1.useConnect)();
202
+ function ConnectUserButton({ variant = "row", onSignOut, className, theme, onThemeChange, }) {
203
+ const { session, isLoading } = (0, provider_js_1.useConnect)();
204
+ const { collapsed } = (0, shell_context_js_1.useConnectShell)();
148
205
  if (isLoading) {
149
- return ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center gap-2 ${className ?? ""}`, children: [(0, jsx_runtime_1.jsx)(react_1.Skeleton, { className: "size-8 shrink-0 rounded-full" }), !compact && (0, jsx_runtime_1.jsx)(react_1.Skeleton, { className: "h-4 w-24" })] }));
206
+ return ((0, jsx_runtime_1.jsxs)("div", { className: `flex h-10 items-center gap-3 ${variant === "row" ? "ps-[4px]" : ""} ${className ?? ""}`, children: [(0, jsx_runtime_1.jsx)(react_1.Skeleton, { className: "size-8 shrink-0 rounded-full" }), variant !== "compact" && !collapsed && (0, jsx_runtime_1.jsx)(react_1.Skeleton, { className: "h-4 w-24" })] }));
150
207
  }
151
208
  const user = session?.user;
152
209
  if (!user)
153
210
  return null;
154
211
  const label = user.displayName ?? user.email ?? "Account";
155
- const role = session?.applicationRole?.role;
156
- const planChip = subscriptionChip(session?.subscription ?? null);
157
- return ((0, jsx_runtime_1.jsxs)(react_1.Modal, { children: [(0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", "aria-label": `Account: ${label}`,
158
- // `pl-1` against the default right padding is optical, not sloppy: a
159
- // circular avatar needs less inset than a text label to look evenly
160
- // seated in the button.
161
- className: `gap-2 pl-1 ${className ?? ""}`, children: [(0, jsx_runtime_1.jsx)(UserAvatar, { user: user, size: "sm" }), !compact && (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-foreground", children: label })] }), (0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { size: "md", scroll: "inside", children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_1.Modal.CloseTrigger, {}), (0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Heading, { children: "Account" }) }), (0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "flex flex-col gap-5", children: [(0, jsx_runtime_1.jsxs)("div", { className: `flex items-start gap-3 rounded-xl border border-border ${surfaces_js_1.PANEL_FILL} p-4`, children: [(0, jsx_runtime_1.jsx)(UserAvatar, { user: user, size: "lg" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm font-semibold text-foreground", children: label }), user.email && (0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: user.email }), (0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: user.workspaceName }), (role || planChip || !user.emailVerified) && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-1.5 flex flex-wrap items-center gap-1.5", children: [role && ((0, jsx_runtime_1.jsx)(react_1.Chip, { size: "sm", variant: "soft", color: "accent", children: humanizeRole(role) })), planChip, !user.emailVerified && ((0, jsx_runtime_1.jsx)(react_1.Chip, { size: "sm", variant: "soft", color: "warning", children: "Email unverified" }))] }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [(0, jsx_runtime_1.jsx)(DestinationTile, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.UserRound, { className: surfaces_js_1.ICON, "aria-hidden": "true" }), label: "Profile", url: profileUrl(appUrl, clientId) }), (0, jsx_runtime_1.jsx)(DestinationTile, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.CreditCard, { className: surfaces_js_1.ICON, "aria-hidden": "true" }), label: "Billing", url: `${appUrl}/billing` }), (0, jsx_runtime_1.jsx)(DestinationTile, { icon: (0, jsx_runtime_1.jsx)(mark_js_1.ConnectMark, { className: surfaces_js_1.ICON }), label: "Connect", url: appUrl })] }), theme && onThemeChange && ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-2", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Appearance" }), (0, jsx_runtime_1.jsx)(react_1.ToggleButtonGroup, { fullWidth: true, "aria-label": "Theme", selectionMode: "single", disallowEmptySelection: true, selectedKeys: [theme], onSelectionChange: (keys) => {
162
- const [next] = [...keys];
163
- if (typeof next === "string")
164
- onThemeChange(next);
165
- }, children: THEME_OPTIONS.map(({ value, label: optionLabel, icon: Icon }, index) => ((0, jsx_runtime_1.jsxs)(react_1.ToggleButton, { id: value, children: [index > 0 && (0, jsx_runtime_1.jsx)(react_1.ToggleButtonGroup.Separator, {}), (0, jsx_runtime_1.jsx)(Icon, { className: "size-4", "aria-hidden": "true" }), optionLabel] }, value))) })] })), (0, jsx_runtime_1.jsx)(account_sessions_js_1.ConnectAccountSessions, {})] }), (0, jsx_runtime_1.jsx)(react_1.Modal.Footer, { children: (0, jsx_runtime_1.jsxs)(react_1.Button, { slot: "close", fullWidth: true, variant: "tertiary", className: "text-danger", onPress: () => {
166
- void signOut().then(onSignOut);
167
- }, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.LogOut, { className: "size-4 shrink-0" }), "Sign out"] }) })] }) }) })] }));
212
+ return ((0, jsx_runtime_1.jsxs)(react_1.Modal, { children: [variant === "row" ? ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", "aria-label": `Account: ${label}`, className: (0, shell_context_js_1.sidebarRowClass)({ tile: true, className }), children: [(0, jsx_runtime_1.jsx)(UserAvatar, { user: user, size: "sm" }), (0, jsx_runtime_1.jsx)(shell_context_js_1.SidebarLabel, { children: (0, jsx_runtime_1.jsxs)("span", { className: "flex min-w-0 flex-col text-start leading-tight", children: [(0, jsx_runtime_1.jsx)("span", { className: "truncate text-sm font-medium text-foreground", children: label }), user.email && user.email !== label && ((0, jsx_runtime_1.jsx)("span", { className: "truncate text-xs font-normal text-muted", children: user.email }))] }) }), (0, jsx_runtime_1.jsx)(motion_js_1.m.span, { className: "shrink-0 text-muted", animate: { opacity: collapsed ? 0 : 1 }, transition: collapsed ? { duration: 0.1 } : { duration: 0.18, delay: 0.1 }, "aria-hidden": true, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4" }) })] })) : ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", "aria-label": `Account: ${label}`,
213
+ // `ps-1` against the default trailing padding is optical, not
214
+ // sloppy: a circular avatar needs less inset than a text label to
215
+ // look evenly seated in the button.
216
+ className: `gap-2 ps-1 ${className ?? ""}`, children: [(0, jsx_runtime_1.jsx)(UserAvatar, { user: user, size: "sm" }), variant === "inline" && (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-foreground", children: label })] })), (0, jsx_runtime_1.jsx)(ProfileSurface, { onSignOut: onSignOut, theme: theme, onThemeChange: onThemeChange })] }));
217
+ }
218
+ /**
219
+ * @deprecated Renamed to `ConnectUserButton` in 0.12 - it is one button that
220
+ * opens a modal, and calling it a "menu" had people looking for a dropdown
221
+ * that never existed. `compact` maps to `variant="compact"`, and the old
222
+ * default (avatar plus name) to `variant="inline"`. This alias will be removed
223
+ * in a later minor.
224
+ */
225
+ function ConnectUserMenu({ compact = false, ...rest }) {
226
+ return (0, jsx_runtime_1.jsx)(ConnectUserButton, { variant: compact ? "compact" : "inline", ...rest });
168
227
  }
169
228
  /**
170
229
  * The "Manage subscription" action on its own, for layouts that want it
@@ -0,0 +1,100 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ConnectWorkspaceSwitcher = ConnectWorkspaceSwitcher;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const react_2 = require("@heroui/react");
8
+ const lucide_react_1 = require("lucide-react");
9
+ const provider_js_1 = require("../provider.js");
10
+ const motion_js_1 = require("./motion.js");
11
+ const shell_context_js_1 = require("./shell-context.js");
12
+ /** `Acme Logistics` -> `AL`. The collapsed rail's whole label for a workspace. */
13
+ function workspaceInitials(name) {
14
+ const parts = name.trim().split(/\s+/).filter(Boolean);
15
+ return (parts.slice(0, 2).map((part) => part[0]).join("") || "?").toUpperCase();
16
+ }
17
+ /**
18
+ * Switches which workspace this application is looking at.
19
+ *
20
+ * **Rendered in the `AppShell` sidebar header by default**, directly
21
+ * under the brand - which workspace you are in scopes everything below it, so
22
+ * it reads as a heading for the nav rather than as one more destination.
23
+ *
24
+ * It decides for itself whether to appear: an account with one workspace is
25
+ * shown nothing, because a switcher offering one option teaches the user
26
+ * nothing and costs a row of chrome on every screen. That is the opposite of
27
+ * Connect's own switcher, which always renders - Connect is where a workspace
28
+ * is *created*, so hiding it at one workspace would guarantee nobody ever
29
+ * reached two. A partner application creates nothing, so it has no such duty.
30
+ *
31
+ * **Authorization is the data, not a prop.** The list comes from the caller's
32
+ * own session (`GET /workspaces` takes no user id), so a workspace the user
33
+ * may not reach is simply not in it. There is deliberately no `roles` prop to
34
+ * pass or forget - the only way to render a row is for Connect to have
35
+ * returned it.
36
+ *
37
+ * Rows the user *belongs to* but cannot use here - a workspace outside the
38
+ * application's audience, or one with no subscription - are shown **disabled
39
+ * with the reason** rather than hidden. Hiding them reads as data loss to the
40
+ * person who owns the workspace; `switch` refuses the same two cases, so the
41
+ * label and the outcome always agree.
42
+ *
43
+ * A switch rotates the session token. The bridge persists the new one in the
44
+ * host application's own session store and the token never reaches this
45
+ * component - so the only thing to do afterwards is reload, which is what
46
+ * `onSwitched` defaults to.
47
+ */
48
+ function ConnectWorkspaceSwitcher({ onSwitched, row = true, className, }) {
49
+ const { mutateResource } = (0, provider_js_1.useConnect)();
50
+ const { collapsed } = (0, shell_context_js_1.useConnectShell)();
51
+ const user = (0, provider_js_1.useConnectUser)();
52
+ const { data, isLoading } = (0, provider_js_1.useConnectWorkspaces)();
53
+ const [switching, setSwitching] = (0, react_1.useState)(null);
54
+ const [error, setError] = (0, react_1.useState)(null);
55
+ const workspaces = data?.workspaces ?? [];
56
+ // Nothing to switch between, or nothing loaded yet. Deliberately renders
57
+ // `null` rather than a skeleton: this sits in persistent chrome, and a
58
+ // placeholder that resolves to nothing for most accounts is worse than
59
+ // appearing a moment late for the few who have two.
60
+ if (isLoading || workspaces.length < 2)
61
+ return null;
62
+ const active = workspaces.find((workspace) => workspace.isActive);
63
+ const label = active?.name ?? user?.workspaceName ?? "Workspace";
64
+ const handleSwitch = async (workspace) => {
65
+ if (workspace.isActive || !selectable(workspace))
66
+ return;
67
+ setSwitching(workspace.workspaceId);
68
+ setError(null);
69
+ try {
70
+ // Through the provider's own bridge caller rather than a bare `fetch`,
71
+ // so this inherits the configured `basePath`, the credentials mode and
72
+ // the error shape every other call in the SDK uses.
73
+ await mutateResource("/workspaces/switch", { workspaceId: workspace.workspaceId });
74
+ if (onSwitched) {
75
+ onSwitched(workspace);
76
+ }
77
+ else {
78
+ window.location.reload();
79
+ }
80
+ }
81
+ catch (cause) {
82
+ setError(cause instanceof Error ? cause.message : "Could not switch workspace");
83
+ setSwitching(null);
84
+ }
85
+ };
86
+ return ((0, jsx_runtime_1.jsxs)("div", { className: row ? "" : className, children: [(0, jsx_runtime_1.jsxs)(react_2.Dropdown, { children: [row ? (
87
+ // A 32px tile rather than a 20px glyph, so the initials land on the
88
+ // same optical centre the avatar below them uses - see
89
+ // `SIDEBAR_ROW_TILE`. Collapsed, this tile *is* the switcher.
90
+ (0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "tertiary", "aria-label": `Workspace: ${label}. Switch workspace`, className: (0, shell_context_js_1.sidebarRowClass)({ tile: true, className }), children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-accent-soft text-xs font-semibold text-accent", children: workspaceInitials(label) }), (0, jsx_runtime_1.jsx)(shell_context_js_1.SidebarLabel, { className: "text-sm font-medium text-foreground", children: label }), (0, jsx_runtime_1.jsx)(motion_js_1.m.span, { className: "shrink-0 text-muted", animate: { opacity: collapsed ? 0 : 1 }, transition: collapsed ? { duration: 0.1 } : { duration: 0.18, delay: 0.1 }, "aria-hidden": true, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4" }) })] })) : ((0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", size: "sm", className: "w-full justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "truncate", children: label }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" })] })), (0, jsx_runtime_1.jsx)(react_2.Dropdown.Popover, { children: (0, jsx_runtime_1.jsx)(react_2.ListBox, { children: workspaces.map((workspace) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: workspace.workspaceId, textValue: workspace.name, isDisabled: !selectable(workspace) || switching !== null, onAction: () => void handleSwitch(workspace), children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: workspace.name }), selectable(workspace) ? null : ((0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: reasonUnavailable(workspace) }))] }), (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, workspace.workspaceId))) }) })] }), error && !collapsed ? (0, jsx_runtime_1.jsx)("p", { className: "mt-1 px-2 text-xs text-danger", children: error }) : null] }));
91
+ }
92
+ /** Belongs to the user, and usable here. The same two facts `switch` enforces. */
93
+ function selectable(workspace) {
94
+ return workspace.inAudience && workspace.subscribed;
95
+ }
96
+ function reasonUnavailable(workspace) {
97
+ if (!workspace.inAudience)
98
+ return "Not available for this kind of workspace";
99
+ return "No active subscription";
100
+ }
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.AI_EMPTY_VALUE = exports.aiStatusTone = exports.isNumericAiFormat = exports.formatAiValue = exports.AiSources = exports.AiSteps = exports.AiDetails = exports.AiTimeline = exports.AiMetrics = exports.AiChart = exports.AiTable = exports.ConnectAskAi = exports.ConnectAppsGrid = exports.gateExemptPaths = exports.isNavItemActive = exports.resolveActivePath = exports.normalizePath = exports.flattenNav = exports.normalizeNav = exports.useActivePath = exports.ConnectSideNav = exports.AppShell = exports.ConnectAccountSessions = exports.ConnectSignOutButton = exports.ConnectHomeButton = exports.ConnectProfileButton = exports.ConnectInvoicesButton = exports.ConnectManageSubscriptionButton = exports.ConnectUserMenu = exports.ConnectRegisterButton = exports.ConnectLoginButton = exports.ConnectLogo = exports.ConnectMark = exports.isConnectError = exports.ConnectError = exports.normalizePlanFeatures = exports.blockingBlockers = exports.CONNECT_APP_URL = exports.useConnectResource = exports.useConnectIntegrations = exports.useConnectSessions = exports.useConnectAddonUsage = exports.useConnectApps = exports.useConnectPlans = exports.useConnectEntitlements = exports.useConnectAudience = exports.useConnectWorkspaceType = exports.useConnectUser = exports.useConnect = exports.ConnectProvider = void 0;
5
- exports.resolvePlanGroup = exports.groupPlansByTier = exports.ACTION_LABEL = exports.discountLabel = exports.annualSaving = exports.resolveAction = exports.monthlyOf = exports.formatPrice = exports.buildCheckoutUrl = exports.useConnectCheckout = exports.ConnectPlansPage = exports.ConnectUsageMeter = exports.ConnectWorkspaceTypeGate = exports.useConnectFeature = exports.ConnectFeatureGate = exports.useConnectAccessGate = exports.ConnectAccessGate = exports.ConnectReadinessGate = exports.ConnectReadinessBanner = exports.resolveSubscriptionState = exports.ConnectSeatUsage = exports.ConnectTrialBanner = exports.ConnectSubscriptionCard = exports.ConnectNoSubscriptionCard = exports.ConnectSubscriptionGate = exports.ConnectSubscribeDialog = exports.ConnectPlans = exports.ConnectIntegrationsModal = exports.AI_TONE_DOT = exports.AI_TONE_CHIP = void 0;
4
+ exports.normalizePath = exports.flattenNav = exports.normalizeNav = exports.useActivePath = exports.ConnectSideNav = exports.ConnectSidebarNav = exports.RAIL_EXPANDED = exports.RAIL_COLLAPSED = exports.sidebarRowClass = exports.useConnectShell = exports.ConnectShellProvider = exports.ConnectSidebarRow = exports.ConnectSidebarFooter = exports.ConnectSidebarBody = exports.ConnectSidebarHeader = exports.ConnectSidebar = exports.AppShell = exports.ConnectAccountSessions = exports.ConnectSignOutButton = exports.ConnectHomeButton = exports.ConnectProfileButton = exports.ConnectInvoicesButton = exports.ConnectManageSubscriptionButton = exports.ConnectUserMenu = exports.ConnectProfileModal = exports.ConnectUserButton = exports.ConnectRegisterButton = exports.ConnectLoginButton = exports.ConnectLogo = exports.ConnectMark = exports.ConnectGrievanceNotice = exports.isConnectError = exports.ConnectError = exports.normalizePlanFeatures = exports.blockingBlockers = exports.CONNECT_APP_URL = exports.useConnectResource = exports.useConnectIntegrations = exports.useConnectSessions = exports.useConnectAddonUsage = exports.useConnectCompanyProfile = exports.useConnectApps = exports.useConnectPlans = exports.useConnectEntitlements = exports.useConnectPlatformAccess = exports.useConnectAudience = exports.useConnectWorkspaceType = exports.useConnectUser = exports.useConnect = exports.ConnectProvider = void 0;
5
+ exports.annualSaving = exports.resolveAction = exports.monthlyOf = exports.formatPrice = exports.buildCheckoutUrl = exports.useConnectCheckout = exports.ConnectFilterTabs = exports.ConnectCursorPagination = exports.ConnectConfirmDialog = exports.ConnectLinkButton = exports.ConnectPlansPage = exports.ConnectUsageMeter = exports.ConnectPlatformGate = exports.ConnectWorkspaceTypeGate = exports.useConnectFeature = exports.ConnectFeatureGate = exports.useConnectAccessGate = exports.ConnectAccessGate = exports.ConnectReadinessGate = exports.ConnectReadinessBanner = exports.resolveSubscriptionState = exports.ConnectSeatUsage = exports.ConnectTrialBanner = exports.ConnectSubscriptionCard = exports.ConnectNoSubscriptionCard = exports.ConnectSubscriptionGate = exports.ConnectSubscribeDialog = exports.ConnectPlans = exports.ConnectIntegrationsModal = exports.AI_TONE_DOT = exports.AI_TONE_CHIP = exports.AI_EMPTY_VALUE = exports.aiStatusTone = exports.isNumericAiFormat = exports.formatAiValue = exports.AiSources = exports.AiSteps = exports.AiDetails = exports.AiTimeline = exports.AiMetrics = exports.AiChart = exports.AiTable = exports.ConnectAskAi = exports.ConnectAppsGrid = exports.isLinkableConnectScreen = exports.matchConnectScreen = exports.filterNavForAudience = exports.gateExemptPaths = exports.isNavItemActive = exports.resolveActivePath = void 0;
6
+ exports.useConnectWorkspaceMembers = exports.useConnectWorkspaces = exports.ConnectWorkspaceSwitcher = exports.resolvePlanGroup = exports.groupPlansByTier = exports.ACTION_LABEL = exports.discountLabel = void 0;
6
7
  /**
7
8
  * Browser half of `@cosmoops/connect-sdk` - React components and hooks.
8
9
  *
@@ -21,9 +22,11 @@ Object.defineProperty(exports, "useConnect", { enumerable: true, get: function (
21
22
  Object.defineProperty(exports, "useConnectUser", { enumerable: true, get: function () { return provider_js_1.useConnectUser; } });
22
23
  Object.defineProperty(exports, "useConnectWorkspaceType", { enumerable: true, get: function () { return provider_js_1.useConnectWorkspaceType; } });
23
24
  Object.defineProperty(exports, "useConnectAudience", { enumerable: true, get: function () { return provider_js_1.useConnectAudience; } });
25
+ Object.defineProperty(exports, "useConnectPlatformAccess", { enumerable: true, get: function () { return provider_js_1.useConnectPlatformAccess; } });
24
26
  Object.defineProperty(exports, "useConnectEntitlements", { enumerable: true, get: function () { return provider_js_1.useConnectEntitlements; } });
25
27
  Object.defineProperty(exports, "useConnectPlans", { enumerable: true, get: function () { return provider_js_1.useConnectPlans; } });
26
28
  Object.defineProperty(exports, "useConnectApps", { enumerable: true, get: function () { return provider_js_1.useConnectApps; } });
29
+ Object.defineProperty(exports, "useConnectCompanyProfile", { enumerable: true, get: function () { return provider_js_1.useConnectCompanyProfile; } });
27
30
  Object.defineProperty(exports, "useConnectAddonUsage", { enumerable: true, get: function () { return provider_js_1.useConnectAddonUsage; } });
28
31
  Object.defineProperty(exports, "useConnectSessions", { enumerable: true, get: function () { return provider_js_1.useConnectSessions; } });
29
32
  Object.defineProperty(exports, "useConnectIntegrations", { enumerable: true, get: function () { return provider_js_1.useConnectIntegrations; } });
@@ -38,6 +41,8 @@ Object.defineProperty(exports, "normalizePlanFeatures", { enumerable: true, get:
38
41
  var errors_js_1 = require("../errors.js");
39
42
  Object.defineProperty(exports, "ConnectError", { enumerable: true, get: function () { return errors_js_1.ConnectError; } });
40
43
  Object.defineProperty(exports, "isConnectError", { enumerable: true, get: function () { return errors_js_1.isConnectError; } });
44
+ var grievance_notice_js_1 = require("./components/grievance-notice.js");
45
+ Object.defineProperty(exports, "ConnectGrievanceNotice", { enumerable: true, get: function () { return grievance_notice_js_1.ConnectGrievanceNotice; } });
41
46
  var mark_js_1 = require("./mark.js");
42
47
  Object.defineProperty(exports, "ConnectMark", { enumerable: true, get: function () { return mark_js_1.ConnectMark; } });
43
48
  var logo_js_1 = require("./components/logo.js");
@@ -46,6 +51,9 @@ var auth_buttons_js_1 = require("./components/auth-buttons.js");
46
51
  Object.defineProperty(exports, "ConnectLoginButton", { enumerable: true, get: function () { return auth_buttons_js_1.ConnectLoginButton; } });
47
52
  Object.defineProperty(exports, "ConnectRegisterButton", { enumerable: true, get: function () { return auth_buttons_js_1.ConnectRegisterButton; } });
48
53
  var user_menu_js_1 = require("./components/user-menu.js");
54
+ Object.defineProperty(exports, "ConnectUserButton", { enumerable: true, get: function () { return user_menu_js_1.ConnectUserButton; } });
55
+ Object.defineProperty(exports, "ConnectProfileModal", { enumerable: true, get: function () { return user_menu_js_1.ConnectProfileModal; } });
56
+ /** @deprecated Renamed to `ConnectUserButton`. */
49
57
  Object.defineProperty(exports, "ConnectUserMenu", { enumerable: true, get: function () { return user_menu_js_1.ConnectUserMenu; } });
50
58
  Object.defineProperty(exports, "ConnectManageSubscriptionButton", { enumerable: true, get: function () { return user_menu_js_1.ConnectManageSubscriptionButton; } });
51
59
  Object.defineProperty(exports, "ConnectInvoicesButton", { enumerable: true, get: function () { return user_menu_js_1.ConnectInvoicesButton; } });
@@ -60,9 +68,37 @@ Object.defineProperty(exports, "ConnectAccountSessions", { enumerable: true, get
60
68
  // pieces it composes rather than in a section of its own - an integration
61
69
  // reaching for a sidebar wants to find the launcher and the account menu in
62
70
  // the same breath, since the shell already mounts both.
71
+ /*
72
+ * `AppShell`, deliberately without the `Connect` prefix every other export
73
+ * here carries. That exception was decided in 0.8.0 and is kept: a host whose
74
+ * own layout component is also called `AppShell` aliases it on import
75
+ * (`import { AppShell as ConnectAppShell }`), which is the documented answer
76
+ * and what both reference integrations do.
77
+ */
63
78
  var app_shell_js_1 = require("./components/app-shell.js");
64
79
  Object.defineProperty(exports, "AppShell", { enumerable: true, get: function () { return app_shell_js_1.AppShell; } });
80
+ /*
81
+ * The rail, in pieces. `AppShell` composes exactly these and nothing
82
+ * else, which is the point of exporting them: an application whose chrome the
83
+ * prop API cannot express rebuilds the same rail from the same parts rather
84
+ * than forking it, and gets the collapse animation, the 32px glyph centre and
85
+ * the mobile drawer for free. They coordinate through `ConnectShellProvider`,
86
+ * so a part dropped in anywhere under one still knows the rail's state.
87
+ */
88
+ var sidebar_js_1 = require("./components/sidebar.js");
89
+ Object.defineProperty(exports, "ConnectSidebar", { enumerable: true, get: function () { return sidebar_js_1.ConnectSidebar; } });
90
+ Object.defineProperty(exports, "ConnectSidebarHeader", { enumerable: true, get: function () { return sidebar_js_1.ConnectSidebarHeader; } });
91
+ Object.defineProperty(exports, "ConnectSidebarBody", { enumerable: true, get: function () { return sidebar_js_1.ConnectSidebarBody; } });
92
+ Object.defineProperty(exports, "ConnectSidebarFooter", { enumerable: true, get: function () { return sidebar_js_1.ConnectSidebarFooter; } });
93
+ Object.defineProperty(exports, "ConnectSidebarRow", { enumerable: true, get: function () { return sidebar_js_1.ConnectSidebarRow; } });
94
+ var shell_context_js_1 = require("./components/shell-context.js");
95
+ Object.defineProperty(exports, "ConnectShellProvider", { enumerable: true, get: function () { return shell_context_js_1.ConnectShellProvider; } });
96
+ Object.defineProperty(exports, "useConnectShell", { enumerable: true, get: function () { return shell_context_js_1.useConnectShell; } });
97
+ Object.defineProperty(exports, "sidebarRowClass", { enumerable: true, get: function () { return shell_context_js_1.sidebarRowClass; } });
98
+ Object.defineProperty(exports, "RAIL_COLLAPSED", { enumerable: true, get: function () { return shell_context_js_1.RAIL_COLLAPSED; } });
99
+ Object.defineProperty(exports, "RAIL_EXPANDED", { enumerable: true, get: function () { return shell_context_js_1.RAIL_EXPANDED; } });
65
100
  var side_nav_js_1 = require("./components/side-nav.js");
101
+ Object.defineProperty(exports, "ConnectSidebarNav", { enumerable: true, get: function () { return side_nav_js_1.ConnectSidebarNav; } });
66
102
  Object.defineProperty(exports, "ConnectSideNav", { enumerable: true, get: function () { return side_nav_js_1.ConnectSideNav; } });
67
103
  var use_active_path_js_1 = require("./use-active-path.js");
68
104
  Object.defineProperty(exports, "useActivePath", { enumerable: true, get: function () { return use_active_path_js_1.useActivePath; } });
@@ -77,6 +113,10 @@ Object.defineProperty(exports, "normalizePath", { enumerable: true, get: functio
77
113
  Object.defineProperty(exports, "resolveActivePath", { enumerable: true, get: function () { return nav_js_1.resolveActivePath; } });
78
114
  Object.defineProperty(exports, "isNavItemActive", { enumerable: true, get: function () { return nav_js_1.isNavItemActive; } });
79
115
  Object.defineProperty(exports, "gateExemptPaths", { enumerable: true, get: function () { return nav_js_1.gateExemptPaths; } });
116
+ Object.defineProperty(exports, "filterNavForAudience", { enumerable: true, get: function () { return nav_js_1.filterNavForAudience; } });
117
+ var ai_screens_js_1 = require("./components/ai-screens.js");
118
+ Object.defineProperty(exports, "matchConnectScreen", { enumerable: true, get: function () { return ai_screens_js_1.matchConnectScreen; } });
119
+ Object.defineProperty(exports, "isLinkableConnectScreen", { enumerable: true, get: function () { return ai_screens_js_1.isLinkableConnectScreen; } });
80
120
  var apps_grid_js_1 = require("./components/apps-grid.js");
81
121
  Object.defineProperty(exports, "ConnectAppsGrid", { enumerable: true, get: function () { return apps_grid_js_1.ConnectAppsGrid; } });
82
122
  // `AppShell askAi` mounts this; exported for an app that wants the panel
@@ -132,10 +172,35 @@ Object.defineProperty(exports, "ConnectFeatureGate", { enumerable: true, get: fu
132
172
  Object.defineProperty(exports, "useConnectFeature", { enumerable: true, get: function () { return feature_gate_js_1.useConnectFeature; } });
133
173
  var workspace_type_gate_js_1 = require("./components/workspace-type-gate.js");
134
174
  Object.defineProperty(exports, "ConnectWorkspaceTypeGate", { enumerable: true, get: function () { return workspace_type_gate_js_1.ConnectWorkspaceTypeGate; } });
175
+ var platform_gate_js_1 = require("./components/platform-gate.js");
176
+ Object.defineProperty(exports, "ConnectPlatformGate", { enumerable: true, get: function () { return platform_gate_js_1.ConnectPlatformGate; } });
135
177
  var usage_meter_js_1 = require("./components/usage-meter.js");
136
178
  Object.defineProperty(exports, "ConnectUsageMeter", { enumerable: true, get: function () { return usage_meter_js_1.ConnectUsageMeter; } });
137
179
  var plans_page_js_1 = require("./components/plans-page.js");
138
180
  Object.defineProperty(exports, "ConnectPlansPage", { enumerable: true, get: function () { return plans_page_js_1.ConnectPlansPage; } });
181
+ /*
182
+ * The interactive half of the page-composition layer. Their presentational
183
+ * counterparts - `ConnectPageShell`, `ConnectSection`, `ConnectEmptyState`,
184
+ * `ConnectStatusChip`, `ConnectSettingRow` - live in
185
+ * `@cosmoops/connect-sdk/ui`, which is deliberately not a `"use client"`
186
+ * module so a page shell does not become a client boundary. These three hold
187
+ * state, so they belong here.
188
+ */
189
+ /*
190
+ * Built and compiled into `dist/` since 0.8 and reachable from no entry point
191
+ * until now - the same oversight `ConnectAccountSessions` had. It is the only
192
+ * supported way to render a link that looks like a button: HeroUI v3's
193
+ * `Button` has no `as` prop, so `<Button as={Link}>` neither type checks nor
194
+ * works.
195
+ */
196
+ var link_button_js_1 = require("./components/link-button.js");
197
+ Object.defineProperty(exports, "ConnectLinkButton", { enumerable: true, get: function () { return link_button_js_1.ConnectLinkButton; } });
198
+ var confirm_dialog_js_1 = require("./components/confirm-dialog.js");
199
+ Object.defineProperty(exports, "ConnectConfirmDialog", { enumerable: true, get: function () { return confirm_dialog_js_1.ConnectConfirmDialog; } });
200
+ var cursor_pagination_js_1 = require("./components/cursor-pagination.js");
201
+ Object.defineProperty(exports, "ConnectCursorPagination", { enumerable: true, get: function () { return cursor_pagination_js_1.ConnectCursorPagination; } });
202
+ var filter_tabs_js_1 = require("./components/filter-tabs.js");
203
+ Object.defineProperty(exports, "ConnectFilterTabs", { enumerable: true, get: function () { return filter_tabs_js_1.ConnectFilterTabs; } });
139
204
  var use_checkout_js_1 = require("./use-checkout.js");
140
205
  Object.defineProperty(exports, "useConnectCheckout", { enumerable: true, get: function () { return use_checkout_js_1.useConnectCheckout; } });
141
206
  Object.defineProperty(exports, "buildCheckoutUrl", { enumerable: true, get: function () { return use_checkout_js_1.buildCheckoutUrl; } });
@@ -153,3 +218,8 @@ Object.defineProperty(exports, "ACTION_LABEL", { enumerable: true, get: function
153
218
  // answer this, and `groupPlansByTier` is how to read it.
154
219
  Object.defineProperty(exports, "groupPlansByTier", { enumerable: true, get: function () { return plan_pricing_js_1.groupPlansByTier; } });
155
220
  Object.defineProperty(exports, "resolvePlanGroup", { enumerable: true, get: function () { return plan_pricing_js_1.resolvePlanGroup; } });
221
+ var workspace_switcher_js_1 = require("./components/workspace-switcher.js");
222
+ Object.defineProperty(exports, "ConnectWorkspaceSwitcher", { enumerable: true, get: function () { return workspace_switcher_js_1.ConnectWorkspaceSwitcher; } });
223
+ var provider_js_2 = require("./provider.js");
224
+ Object.defineProperty(exports, "useConnectWorkspaces", { enumerable: true, get: function () { return provider_js_2.useConnectWorkspaces; } });
225
+ Object.defineProperty(exports, "useConnectWorkspaceMembers", { enumerable: true, get: function () { return provider_js_2.useConnectWorkspaceMembers; } });
@@ -7,10 +7,14 @@ exports.useConnect = useConnect;
7
7
  exports.useConnectUser = useConnectUser;
8
8
  exports.useConnectWorkspaceType = useConnectWorkspaceType;
9
9
  exports.useConnectAudience = useConnectAudience;
10
+ exports.useConnectPlatformAccess = useConnectPlatformAccess;
10
11
  exports.useConnectEntitlements = useConnectEntitlements;
11
12
  exports.useConnectAddonUsage = useConnectAddonUsage;
12
13
  exports.useConnectPlans = useConnectPlans;
14
+ exports.useConnectCompanyProfile = useConnectCompanyProfile;
13
15
  exports.useConnectApps = useConnectApps;
16
+ exports.useConnectWorkspaces = useConnectWorkspaces;
17
+ exports.useConnectWorkspaceMembers = useConnectWorkspaceMembers;
14
18
  exports.useConnectIntegrations = useConnectIntegrations;
15
19
  exports.useConnectSessions = useConnectSessions;
16
20
  exports.useConnectResource = useConnectResource;
@@ -151,7 +155,7 @@ function ConnectProvider({ clientId = "", basePath = "/api/connect", redirectUri
151
155
  // error, so a Login button still renders rather than the UI hanging on
152
156
  // skeletons forever.
153
157
  setResult({
154
- session: { user: null, subscription: null, applicationRole: null, audience: null },
158
+ session: { user: null, subscription: null, role: null, permissions: [], staff: false, audience: null },
155
159
  error: cause instanceof Error ? cause.message : "Could not reach Connect",
156
160
  });
157
161
  }
@@ -200,7 +204,7 @@ function ConnectProvider({ clientId = "", basePath = "/api/connect", redirectUri
200
204
  credentials: "same-origin",
201
205
  }).catch(() => { });
202
206
  cache.current.clear();
203
- setResult({ session: { user: null, subscription: null, applicationRole: null, audience: null }, error: null });
207
+ setResult({ session: { user: null, subscription: null, role: null, permissions: [], staff: false, audience: null }, error: null });
204
208
  }, [basePath, fetchImpl]);
205
209
  const value = (0, react_1.useMemo)(() => ({
206
210
  clientId,
@@ -267,6 +271,26 @@ function useConnectWorkspaceType() {
267
271
  function useConnectAudience() {
268
272
  return useConnect().session?.audience ?? null;
269
273
  }
274
+ /**
275
+ * Whether this session is one of Connect's own operators - see
276
+ * `SessionAccess.staff`.
277
+ *
278
+ * `isStaff` is the branch you want; `roles` narrows further when your operator
279
+ * console has tiers of its own (a `developer` seeing a read-only view of what
280
+ * a `platform_admin` can edit, say).
281
+ *
282
+ * **A rendering decision, not an access decision.** The browser can call this
283
+ * whatever it likes. Everything an operator console exposes must be re-checked
284
+ * server-side against `staff` from
285
+ * `client.identity.getUser(sessionToken)` - see `<ConnectPlatformGate>`, which
286
+ * says the same thing at more length.
287
+ */
288
+ function useConnectPlatformAccess() {
289
+ const { session, isLoading } = useConnect();
290
+ const staff = session?.staff === true;
291
+ const role = session?.role ?? null;
292
+ return { isStaff: staff, roles: staff && role ? [role] : [], isLoading };
293
+ }
270
294
  /** Readiness gates and the live subscription, fetched on first use. */
271
295
  function useConnectEntitlements() {
272
296
  return useConnectResource("/entitlements");
@@ -285,10 +309,41 @@ function useConnectAddonUsage() {
285
309
  function useConnectPlans() {
286
310
  return useConnectResource("/plans");
287
311
  }
312
+ /**
313
+ * The platform's legal identity and DPDP grievance channel.
314
+ *
315
+ * The one read here that does not depend on a session, so it resolves the
316
+ * same for a signed-out visitor - which is deliberate, because the moment a
317
+ * grievance channel most needs to be readable is on a public privacy page.
318
+ *
319
+ * Read it rather than copying it into your own configuration; see
320
+ * `CompanyProfile`. `<ConnectGrievanceNotice>` renders it for you.
321
+ */
322
+ function useConnectCompanyProfile() {
323
+ return useConnectResource("/company-profile");
324
+ }
288
325
  /** The apps this workspace can open. */
289
326
  function useConnectApps() {
290
327
  return useConnectResource("/apps");
291
328
  }
329
+ /**
330
+ * The workspaces this user belongs to - what `<ConnectWorkspaceSwitcher>`
331
+ * renders.
332
+ *
333
+ * Empty or single-entry results are normal, not errors: most accounts have one
334
+ * workspace, and the switcher decides for itself whether that is worth
335
+ * drawing.
336
+ */
337
+ function useConnectWorkspaces() {
338
+ return useConnectResource("/workspaces");
339
+ }
340
+ /**
341
+ * The team of the workspace this session is on, with each member's permissions
342
+ * already resolved for this application.
343
+ */
344
+ function useConnectWorkspaceMembers() {
345
+ return useConnectResource("/workspaces/members");
346
+ }
292
347
  /** Enabled Composio-hosted toolkits and this workspace's connection status for each. */
293
348
  function useConnectIntegrations() {
294
349
  return useConnectResource("/integrations");
@@ -157,15 +157,19 @@ function buildCancelUrl({ appUrl, subscriptionId }) {
157
157
  * The Connect URL that completes a subscribe or a plan change.
158
158
  *
159
159
  * Both land on a modal Connect renders over the relevant page (an intercepted
160
- * parallel route - `app/@modal/(.)applications/...` and
161
- * `app/@modal/(.)billing/...`), so the customer arrives already looking at the
162
- * confirmation for the plan they picked here, not at a catalogue they have to
163
- * find it in again.
160
+ * parallel route - `app/@modal/(.)apps/...` and `app/@modal/(.)billing/...`),
161
+ * so the customer arrives already looking at the confirmation for the plan
162
+ * they picked here, not at a catalogue they have to find it in again.
163
+ *
164
+ * The subscribe path is `/apps/...`, not `/applications/...`: the portal
165
+ * route customers see is `/apps`, and `/applications` is a Console-only
166
+ * prefix that has never served this page. Building the other one is what
167
+ * sent every first-time subscriber from a partner's pricing page to a 404.
164
168
  */
165
169
  function buildCheckoutUrl({ appUrl, applicationId, planId, subscriptionId, coupon, }) {
166
170
  const path = subscriptionId
167
171
  ? `/billing/subscriptions/${encodeURIComponent(subscriptionId)}/change-plan/${encodeURIComponent(planId)}`
168
- : `/applications/${encodeURIComponent(applicationId)}/subscribe/${encodeURIComponent(planId)}`;
172
+ : `/apps/${encodeURIComponent(applicationId)}/subscribe/${encodeURIComponent(planId)}`;
169
173
  const url = new URL(path, appUrl);
170
174
  if (coupon?.trim())
171
175
  url.searchParams.set("coupon", coupon.trim());
@@ -59,7 +59,7 @@ class ConnectError extends Error {
59
59
  * guessing keys. These two are the exception: reaching them required
60
60
  * presenting a key whose hash matched, so you already hold it, and what you
61
61
  * need to know is that no retry and no scope change will fix this - the key
62
- * has to be reissued from the application's Integration tab.
62
+ * has to be reissued from the application's Connect client section.
63
63
  *
64
64
  * Worth alerting on. It is the one failure that will not resolve itself and
65
65
  * takes down every call your integration makes.
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizePlanFeatures = exports.blockingBlockers = exports.isConnectError = exports.ConnectError = void 0;
3
+ exports.storeIndexCovers = exports.describeQueryRejection = exports.unchangedSince = exports.storeIndexSignature = exports.storeIndexFor = exports.serverTimestamp = exports.increment = exports.documentExists = exports.deleteField = exports.arrayUnion = exports.arrayRemove = exports.normalizePlanFeatures = exports.blockingBlockers = exports.isConnectError = exports.ConnectError = void 0;
4
4
  /**
5
5
  * `@cosmoops/connect-sdk` - the official client for the CosmoOps Connect
6
6
  * **application-partner API** (`/api/v1/application/*`).
@@ -13,6 +13,8 @@ exports.normalizePlanFeatures = exports.blockingBlockers = exports.isConnectErro
13
13
  * Holds the API key. Never import this from browser code.
14
14
  * - `@cosmoops/connect-sdk/client` - React components and hooks. Talks to your
15
15
  * own origin only.
16
+ * - `@cosmoops/connect-sdk/mcp` - your store's declaration, derived into MCP
17
+ * tools. Holds the API key; never import this from browser code.
16
18
  */
17
19
  var errors_js_1 = require("./errors.js");
18
20
  Object.defineProperty(exports, "ConnectError", { enumerable: true, get: function () { return errors_js_1.ConnectError; } });
@@ -20,3 +22,16 @@ Object.defineProperty(exports, "isConnectError", { enumerable: true, get: functi
20
22
  var helpers_js_1 = require("./helpers.js");
21
23
  Object.defineProperty(exports, "blockingBlockers", { enumerable: true, get: function () { return helpers_js_1.blockingBlockers; } });
22
24
  Object.defineProperty(exports, "normalizePlanFeatures", { enumerable: true, get: function () { return helpers_js_1.normalizePlanFeatures; } });
25
+ var store_js_1 = require("./store.js");
26
+ Object.defineProperty(exports, "arrayRemove", { enumerable: true, get: function () { return store_js_1.arrayRemove; } });
27
+ Object.defineProperty(exports, "arrayUnion", { enumerable: true, get: function () { return store_js_1.arrayUnion; } });
28
+ Object.defineProperty(exports, "deleteField", { enumerable: true, get: function () { return store_js_1.deleteField; } });
29
+ Object.defineProperty(exports, "documentExists", { enumerable: true, get: function () { return store_js_1.documentExists; } });
30
+ Object.defineProperty(exports, "increment", { enumerable: true, get: function () { return store_js_1.increment; } });
31
+ Object.defineProperty(exports, "serverTimestamp", { enumerable: true, get: function () { return store_js_1.serverTimestamp; } });
32
+ Object.defineProperty(exports, "storeIndexFor", { enumerable: true, get: function () { return store_js_1.storeIndexFor; } });
33
+ Object.defineProperty(exports, "storeIndexSignature", { enumerable: true, get: function () { return store_js_1.storeIndexSignature; } });
34
+ Object.defineProperty(exports, "unchangedSince", { enumerable: true, get: function () { return store_js_1.unchangedSince; } });
35
+ var store_schema_js_1 = require("./store-schema.js");
36
+ Object.defineProperty(exports, "describeQueryRejection", { enumerable: true, get: function () { return store_schema_js_1.describeQueryRejection; } });
37
+ Object.defineProperty(exports, "storeIndexCovers", { enumerable: true, get: function () { return store_schema_js_1.storeIndexCovers; } });