@cosmoops/connect-sdk 0.6.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +165 -0
- package/README.md +273 -91
- package/dist/cjs/cli/doctor.js +3 -3
- package/dist/cjs/cli/init.js +3 -3
- package/dist/cjs/client/components/access-gate.js +42 -7
- package/dist/cjs/client/components/account-sessions.js +3 -3
- package/dist/cjs/client/components/ai-format.js +135 -0
- package/dist/cjs/client/components/ai-widgets.js +232 -0
- package/dist/cjs/client/components/app-shell.js +9 -8
- package/dist/cjs/client/components/apps-grid.js +3 -3
- package/dist/cjs/client/components/ask-ai.js +334 -0
- package/dist/cjs/client/components/feature-gate.js +2 -2
- package/dist/cjs/client/components/integrations-modal.js +1 -1
- package/dist/cjs/client/components/logo.js +3 -3
- package/dist/cjs/client/components/nav.js +1 -1
- package/dist/cjs/client/components/plan-pricing.js +1 -1
- package/dist/cjs/client/components/plans.js +6 -6
- package/dist/cjs/client/components/readiness.js +3 -3
- package/dist/cjs/client/components/side-nav.js +1 -1
- package/dist/cjs/client/components/subscribe-dialog.js +2 -2
- package/dist/cjs/client/components/subscription.js +1 -1
- package/dist/cjs/client/components/usage-meter.js +1 -1
- package/dist/cjs/client/components/user-menu.js +31 -8
- package/dist/cjs/client/components/workspace-type-gate.js +48 -0
- package/dist/cjs/client/index.js +32 -4
- package/dist/cjs/client/mark.js +1 -1
- package/dist/cjs/client/provider.js +50 -7
- package/dist/cjs/client/use-checkout.js +3 -3
- package/dist/cjs/errors.js +4 -4
- package/dist/cjs/helpers.js +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/next/index.js +2 -2
- package/dist/cjs/server/client.js +15 -15
- package/dist/cjs/server/handler.js +56 -13
- package/dist/cjs/server/http.js +47 -25
- package/dist/cjs/server/resources/access.js +3 -3
- package/dist/cjs/server/resources/ai.js +1 -1
- package/dist/cjs/server/resources/analytics.js +2 -2
- package/dist/cjs/server/resources/apps.js +4 -4
- package/dist/cjs/server/resources/billing.js +5 -5
- package/dist/cjs/server/resources/drive.js +4 -4
- package/dist/cjs/server/resources/entitlements.js +48 -7
- package/dist/cjs/server/resources/identity.js +6 -4
- package/dist/cjs/server/resources/integrations.js +13 -13
- package/dist/cjs/server/resources/logs.js +3 -3
- package/dist/cjs/server/resources/sessions.js +6 -6
- package/dist/cjs/server/webhooks.js +4 -4
- package/dist/cjs/types.js +1 -1
- package/dist/esm/cli/doctor.js +3 -3
- package/dist/esm/cli/init.js +3 -3
- package/dist/esm/client/components/access-gate.js +43 -8
- package/dist/esm/client/components/account-sessions.js +3 -3
- package/dist/esm/client/components/ai-format.js +129 -0
- package/dist/esm/client/components/ai-widgets.js +223 -0
- package/dist/esm/client/components/app-shell.js +8 -7
- package/dist/esm/client/components/apps-grid.js +3 -3
- package/dist/esm/client/components/ask-ai.js +331 -0
- package/dist/esm/client/components/feature-gate.js +2 -2
- package/dist/esm/client/components/integrations-modal.js +1 -1
- package/dist/esm/client/components/logo.js +3 -3
- package/dist/esm/client/components/nav.js +1 -1
- package/dist/esm/client/components/plan-pricing.js +1 -1
- package/dist/esm/client/components/plans.js +6 -6
- package/dist/esm/client/components/readiness.js +3 -3
- package/dist/esm/client/components/side-nav.js +1 -1
- package/dist/esm/client/components/subscribe-dialog.js +2 -2
- package/dist/esm/client/components/subscription.js +1 -1
- package/dist/esm/client/components/usage-meter.js +1 -1
- package/dist/esm/client/components/user-menu.js +31 -8
- package/dist/esm/client/components/workspace-type-gate.js +45 -0
- package/dist/esm/client/index.js +14 -3
- package/dist/esm/client/mark.js +1 -1
- package/dist/esm/client/provider.js +48 -7
- package/dist/esm/client/use-checkout.js +3 -3
- package/dist/esm/errors.js +4 -4
- package/dist/esm/helpers.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/next/index.js +2 -2
- package/dist/esm/server/client.js +15 -15
- package/dist/esm/server/handler.js +56 -13
- package/dist/esm/server/http.js +47 -25
- package/dist/esm/server/resources/access.js +3 -3
- package/dist/esm/server/resources/ai.js +1 -1
- package/dist/esm/server/resources/analytics.js +2 -2
- package/dist/esm/server/resources/apps.js +4 -4
- package/dist/esm/server/resources/billing.js +5 -5
- package/dist/esm/server/resources/drive.js +4 -4
- package/dist/esm/server/resources/entitlements.js +48 -7
- package/dist/esm/server/resources/identity.js +6 -4
- package/dist/esm/server/resources/integrations.js +13 -13
- package/dist/esm/server/resources/logs.js +3 -3
- package/dist/esm/server/resources/sessions.js +6 -6
- package/dist/esm/server/webhooks.js +4 -4
- package/dist/esm/types.js +1 -1
- package/dist/theme.css +87 -0
- package/dist/types/client/components/access-gate.d.ts +18 -4
- package/dist/types/client/components/account-sessions.d.ts +1 -1
- package/dist/types/client/components/ai-format.d.ts +24 -0
- package/dist/types/client/components/ai-widgets.d.ts +42 -0
- package/dist/types/client/components/app-shell.d.ts +25 -9
- package/dist/types/client/components/apps-grid.d.ts +1 -1
- package/dist/types/client/components/ask-ai.d.ts +73 -0
- package/dist/types/client/components/feature-gate.d.ts +1 -1
- package/dist/types/client/components/integrations-modal.d.ts +1 -1
- package/dist/types/client/components/logo.d.ts +5 -5
- package/dist/types/client/components/nav.d.ts +3 -3
- package/dist/types/client/components/plan-pricing.d.ts +2 -2
- package/dist/types/client/components/readiness.d.ts +2 -2
- package/dist/types/client/components/side-nav.d.ts +1 -1
- package/dist/types/client/components/subscribe-dialog.d.ts +3 -3
- package/dist/types/client/components/usage-meter.d.ts +1 -1
- package/dist/types/client/components/user-menu.d.ts +4 -3
- package/dist/types/client/components/workspace-type-gate.d.ts +48 -0
- package/dist/types/client/index.d.ts +11 -4
- package/dist/types/client/mark.d.ts +1 -1
- package/dist/types/client/provider.d.ts +52 -9
- package/dist/types/client/use-checkout.d.ts +3 -3
- package/dist/types/errors.d.ts +4 -4
- package/dist/types/helpers.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/next/index.d.ts +1 -1
- package/dist/types/server/client.d.ts +13 -13
- package/dist/types/server/handler.d.ts +2 -2
- package/dist/types/server/http.d.ts +6 -1
- package/dist/types/server/resources/access.d.ts +3 -3
- package/dist/types/server/resources/ai.d.ts +2 -2
- package/dist/types/server/resources/analytics.d.ts +2 -2
- package/dist/types/server/resources/apps.d.ts +4 -4
- package/dist/types/server/resources/billing.d.ts +4 -4
- package/dist/types/server/resources/drive.d.ts +4 -4
- package/dist/types/server/resources/entitlements.d.ts +37 -5
- package/dist/types/server/resources/identity.d.ts +2 -2
- package/dist/types/server/resources/integrations.d.ts +9 -9
- package/dist/types/server/resources/logs.d.ts +3 -3
- package/dist/types/server/resources/sessions.d.ts +5 -5
- package/dist/types/server/webhooks.d.ts +4 -4
- package/dist/types/types.d.ts +235 -61
- package/package.json +8 -2
|
@@ -27,6 +27,28 @@ function initials(name, email) {
|
|
|
27
27
|
function openExternal(url) {
|
|
28
28
|
window.open(url, "_blank", "noopener,noreferrer");
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Connect's profile page, with the context it needs to offer a way back here.
|
|
32
|
+
*
|
|
33
|
+
* Connect answers `/profile` for any signed-in user, so the bare URL works on
|
|
34
|
+
* its own - these two params only add the "Back to <your app>" banner. It
|
|
35
|
+
* validates `return_to` against this `client_id`'s registered redirect
|
|
36
|
+
* origins and silently drops the banner if they don't match, so a wrong or
|
|
37
|
+
* stale `appUrl`/`clientId` pairing degrades to what it did before rather
|
|
38
|
+
* than erroring.
|
|
39
|
+
*
|
|
40
|
+
* `window.location.href` rather than a configured home URL: the point is to
|
|
41
|
+
* come back to the page they left, and only the browser knows which one that
|
|
42
|
+
* is.
|
|
43
|
+
*/
|
|
44
|
+
function profileUrl(appUrl, clientId) {
|
|
45
|
+
const url = new URL("/profile", appUrl);
|
|
46
|
+
url.searchParams.set("client_id", clientId);
|
|
47
|
+
if (typeof window !== "undefined") {
|
|
48
|
+
url.searchParams.set("return_to", window.location.href);
|
|
49
|
+
}
|
|
50
|
+
return url.toString();
|
|
51
|
+
}
|
|
30
52
|
/**
|
|
31
53
|
* The member's photo, with their initials underneath it.
|
|
32
54
|
*
|
|
@@ -105,7 +127,7 @@ function DestinationTile({ icon, label, url, }) {
|
|
|
105
127
|
* in place, which is why it sits alone in the footer rather than as a fourth
|
|
106
128
|
* item in the same list as three navigations.
|
|
107
129
|
*
|
|
108
|
-
* The session list is account-wide, not scoped to this
|
|
130
|
+
* The session list is account-wide, not scoped to this application - see
|
|
109
131
|
* `ConnectUserSession`'s doc comment in `types.ts`. Appearance only renders
|
|
110
132
|
* when both `theme` and `onThemeChange` are supplied.
|
|
111
133
|
*
|
|
@@ -122,7 +144,7 @@ function DestinationTile({ icon, label, url, }) {
|
|
|
122
144
|
* the modal programmatically; sign-out uses `slot="close"` instead.
|
|
123
145
|
*/
|
|
124
146
|
function ConnectUserMenu({ compact = false, onSignOut, className, theme, onThemeChange, }) {
|
|
125
|
-
const { session, isLoading, signOut, appUrl } = (0, provider_js_1.useConnect)();
|
|
147
|
+
const { session, isLoading, signOut, appUrl, clientId } = (0, provider_js_1.useConnect)();
|
|
126
148
|
if (isLoading) {
|
|
127
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" })] }));
|
|
128
150
|
}
|
|
@@ -130,13 +152,13 @@ function ConnectUserMenu({ compact = false, onSignOut, className, theme, onTheme
|
|
|
130
152
|
if (!user)
|
|
131
153
|
return null;
|
|
132
154
|
const label = user.displayName ?? user.email ?? "Account";
|
|
133
|
-
const role = session?.
|
|
155
|
+
const role = session?.applicationRole?.role;
|
|
134
156
|
const planChip = subscriptionChip(session?.subscription ?? null);
|
|
135
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}`,
|
|
136
158
|
// `pl-1` against the default right padding is optical, not sloppy: a
|
|
137
159
|
// circular avatar needs less inset than a text label to look evenly
|
|
138
160
|
// seated in the button.
|
|
139
|
-
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.
|
|
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) => {
|
|
140
162
|
const [next] = [...keys];
|
|
141
163
|
if (typeof next === "string")
|
|
142
164
|
onThemeChange(next);
|
|
@@ -157,7 +179,7 @@ function ConnectManageSubscriptionButton({ className }) {
|
|
|
157
179
|
* Invoices and payment history, on Connect.
|
|
158
180
|
*
|
|
159
181
|
* A deep link rather than a component that lists them, and that is not a
|
|
160
|
-
* placeholder: nothing in `/api/v1/
|
|
182
|
+
* placeholder: nothing in `/api/v1/application/*` exposes invoices, so an SDK-side
|
|
161
183
|
* list would have nothing to read. It is also the right answer regardless -
|
|
162
184
|
* a GST tax invoice is a legal document with a fixed format, downloadable and
|
|
163
185
|
* emailed from one place, and a partner-rendered copy of one would be a second
|
|
@@ -175,13 +197,14 @@ function ConnectInvoicesButton({ className }) {
|
|
|
175
197
|
* `ConnectHomeButton`.
|
|
176
198
|
*
|
|
177
199
|
* It was reachable only from inside `ConnectUserMenu`, which meant an app that
|
|
178
|
-
* builds its own account menu had to hardcode `${appUrl}/profile` to offer it
|
|
200
|
+
* builds its own account menu had to hardcode `${appUrl}/profile` to offer it -
|
|
201
|
+
* and got no `return_to`, so the user had no way back.
|
|
179
202
|
*/
|
|
180
203
|
function ConnectProfileButton({ className }) {
|
|
181
|
-
const { session, appUrl } = (0, provider_js_1.useConnect)();
|
|
204
|
+
const { session, appUrl, clientId } = (0, provider_js_1.useConnect)();
|
|
182
205
|
if (!session?.user)
|
|
183
206
|
return null;
|
|
184
|
-
return ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", className: className, onPress: () => openExternal(
|
|
207
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", className: className, onPress: () => openExternal(profileUrl(appUrl, clientId)), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.UserRound, { className: "size-4 shrink-0" }), "Profile", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5 shrink-0 text-muted" })] }));
|
|
185
208
|
}
|
|
186
209
|
/** A plain "Open Connect" button, for a footer or a help menu. */
|
|
187
210
|
function ConnectHomeButton({ className }) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ConnectWorkspaceTypeGate = ConnectWorkspaceTypeGate;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const provider_js_1 = require("../provider.js");
|
|
7
|
+
/**
|
|
8
|
+
* Renders `children` only for a workspace of the given type.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <ConnectWorkspaceTypeGate type="business">
|
|
12
|
+
* <TeamMembersTable />
|
|
13
|
+
* </ConnectWorkspaceTypeGate>
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* The point is dead controls, not access: an `"individual"` workspace has
|
|
17
|
+
* exactly one seat and can never have another, so a members table, an invite
|
|
18
|
+
* button and a seat meter are all things that can only ever fail there.
|
|
19
|
+
* Hiding them is a better answer than letting someone press one and read an
|
|
20
|
+
* error about an account shape they did not choose.
|
|
21
|
+
*
|
|
22
|
+
* This is emphatically **not** the audience gate. It does not know what your
|
|
23
|
+
* application is sold to and will happily render a business-only subtree to a
|
|
24
|
+
* business workspace that has no business being in your application at all -
|
|
25
|
+
* that is `<ConnectAccessGate>`'s job, off the `audience_mismatch` readiness
|
|
26
|
+
* blocker. Use this one *inside* an application both audiences can reach, to
|
|
27
|
+
* vary what each sees.
|
|
28
|
+
*
|
|
29
|
+
* Like every gate in this package it is a **UX gate, not a security
|
|
30
|
+
* boundary**: the browser can ignore it. Anything that must not happen for an
|
|
31
|
+
* individual workspace has to be refused on your server too.
|
|
32
|
+
*
|
|
33
|
+
* Renders `loading` - not `fallback` - while the session is unknown, so a
|
|
34
|
+
* business account never flickers through the individual-account layout.
|
|
35
|
+
*/
|
|
36
|
+
function ConnectWorkspaceTypeGate({ type, children, fallback = null, loading = null, }) {
|
|
37
|
+
const { session, isLoading } = (0, provider_js_1.useConnect)();
|
|
38
|
+
const workspaceType = session?.user?.workspaceType ?? null;
|
|
39
|
+
if (isLoading)
|
|
40
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: loading });
|
|
41
|
+
// Signed out is not "the other type" - there is no workspace to be a type
|
|
42
|
+
// of yet, and rendering the fallback here would put an individual-account
|
|
43
|
+
// layout in front of a visitor who has not signed in.
|
|
44
|
+
if (!workspaceType)
|
|
45
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: loading });
|
|
46
|
+
const accepted = Array.isArray(type) ? type : [type];
|
|
47
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: accepted.includes(workspaceType) ? children : fallback });
|
|
48
|
+
}
|
package/dist/cjs/client/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.
|
|
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.useConnectFeature = void 0;
|
|
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;
|
|
6
6
|
/**
|
|
7
7
|
* Browser half of `@cosmoops/connect-sdk` - React components and hooks.
|
|
8
8
|
*
|
|
@@ -12,13 +12,15 @@ exports.resolvePlanGroup = exports.groupPlansByTier = exports.ACTION_LABEL = exp
|
|
|
12
12
|
*
|
|
13
13
|
* Styling is HeroUI v3 semantic tokens only (`accent`, `muted`, `surface`,
|
|
14
14
|
* `separator`, ...), never a hardcoded colour - so these components inherit
|
|
15
|
-
* whatever theme the host
|
|
15
|
+
* whatever theme the host application platform already configured, and one theme
|
|
16
16
|
* change lands identically in all of them.
|
|
17
17
|
*/
|
|
18
18
|
var provider_js_1 = require("./provider.js");
|
|
19
19
|
Object.defineProperty(exports, "ConnectProvider", { enumerable: true, get: function () { return provider_js_1.ConnectProvider; } });
|
|
20
20
|
Object.defineProperty(exports, "useConnect", { enumerable: true, get: function () { return provider_js_1.useConnect; } });
|
|
21
21
|
Object.defineProperty(exports, "useConnectUser", { enumerable: true, get: function () { return provider_js_1.useConnectUser; } });
|
|
22
|
+
Object.defineProperty(exports, "useConnectWorkspaceType", { enumerable: true, get: function () { return provider_js_1.useConnectWorkspaceType; } });
|
|
23
|
+
Object.defineProperty(exports, "useConnectAudience", { enumerable: true, get: function () { return provider_js_1.useConnectAudience; } });
|
|
22
24
|
Object.defineProperty(exports, "useConnectEntitlements", { enumerable: true, get: function () { return provider_js_1.useConnectEntitlements; } });
|
|
23
25
|
Object.defineProperty(exports, "useConnectPlans", { enumerable: true, get: function () { return provider_js_1.useConnectPlans; } });
|
|
24
26
|
Object.defineProperty(exports, "useConnectApps", { enumerable: true, get: function () { return provider_js_1.useConnectApps; } });
|
|
@@ -59,7 +61,7 @@ Object.defineProperty(exports, "ConnectAccountSessions", { enumerable: true, get
|
|
|
59
61
|
// reaching for a sidebar wants to find the launcher and the account menu in
|
|
60
62
|
// the same breath, since the shell already mounts both.
|
|
61
63
|
var app_shell_js_1 = require("./components/app-shell.js");
|
|
62
|
-
Object.defineProperty(exports, "
|
|
64
|
+
Object.defineProperty(exports, "AppShell", { enumerable: true, get: function () { return app_shell_js_1.AppShell; } });
|
|
63
65
|
var side_nav_js_1 = require("./components/side-nav.js");
|
|
64
66
|
Object.defineProperty(exports, "ConnectSideNav", { enumerable: true, get: function () { return side_nav_js_1.ConnectSideNav; } });
|
|
65
67
|
var use_active_path_js_1 = require("./use-active-path.js");
|
|
@@ -77,6 +79,30 @@ Object.defineProperty(exports, "isNavItemActive", { enumerable: true, get: funct
|
|
|
77
79
|
Object.defineProperty(exports, "gateExemptPaths", { enumerable: true, get: function () { return nav_js_1.gateExemptPaths; } });
|
|
78
80
|
var apps_grid_js_1 = require("./components/apps-grid.js");
|
|
79
81
|
Object.defineProperty(exports, "ConnectAppsGrid", { enumerable: true, get: function () { return apps_grid_js_1.ConnectAppsGrid; } });
|
|
82
|
+
// `AppShell askAi` mounts this; exported for an app that wants the panel
|
|
83
|
+
// without the frame, or wants it somewhere other than the shell's flex row
|
|
84
|
+
// (at which point it overlays rather than pushes - see its doc comment).
|
|
85
|
+
var ask_ai_js_1 = require("./components/ask-ai.js");
|
|
86
|
+
Object.defineProperty(exports, "ConnectAskAi", { enumerable: true, get: function () { return ask_ai_js_1.ConnectAskAi; } });
|
|
87
|
+
// The seven widgets an Ask AI answer can draw, and the formatter they share -
|
|
88
|
+
// exported so a partner rendering Connect's chat somewhere else entirely (a
|
|
89
|
+
// support console, an embedded assistant) gets the same table, chart and metric
|
|
90
|
+
// tiles rather than reimplementing them from the wire types.
|
|
91
|
+
var ai_widgets_js_1 = require("./components/ai-widgets.js");
|
|
92
|
+
Object.defineProperty(exports, "AiTable", { enumerable: true, get: function () { return ai_widgets_js_1.AiTable; } });
|
|
93
|
+
Object.defineProperty(exports, "AiChart", { enumerable: true, get: function () { return ai_widgets_js_1.AiChart; } });
|
|
94
|
+
Object.defineProperty(exports, "AiMetrics", { enumerable: true, get: function () { return ai_widgets_js_1.AiMetrics; } });
|
|
95
|
+
Object.defineProperty(exports, "AiTimeline", { enumerable: true, get: function () { return ai_widgets_js_1.AiTimeline; } });
|
|
96
|
+
Object.defineProperty(exports, "AiDetails", { enumerable: true, get: function () { return ai_widgets_js_1.AiDetails; } });
|
|
97
|
+
Object.defineProperty(exports, "AiSteps", { enumerable: true, get: function () { return ai_widgets_js_1.AiSteps; } });
|
|
98
|
+
Object.defineProperty(exports, "AiSources", { enumerable: true, get: function () { return ai_widgets_js_1.AiSources; } });
|
|
99
|
+
var ai_format_js_1 = require("./components/ai-format.js");
|
|
100
|
+
Object.defineProperty(exports, "formatAiValue", { enumerable: true, get: function () { return ai_format_js_1.formatAiValue; } });
|
|
101
|
+
Object.defineProperty(exports, "isNumericAiFormat", { enumerable: true, get: function () { return ai_format_js_1.isNumericAiFormat; } });
|
|
102
|
+
Object.defineProperty(exports, "aiStatusTone", { enumerable: true, get: function () { return ai_format_js_1.aiStatusTone; } });
|
|
103
|
+
Object.defineProperty(exports, "AI_EMPTY_VALUE", { enumerable: true, get: function () { return ai_format_js_1.AI_EMPTY_VALUE; } });
|
|
104
|
+
Object.defineProperty(exports, "AI_TONE_CHIP", { enumerable: true, get: function () { return ai_format_js_1.AI_TONE_CHIP; } });
|
|
105
|
+
Object.defineProperty(exports, "AI_TONE_DOT", { enumerable: true, get: function () { return ai_format_js_1.AI_TONE_DOT; } });
|
|
80
106
|
var integrations_modal_js_1 = require("./components/integrations-modal.js");
|
|
81
107
|
Object.defineProperty(exports, "ConnectIntegrationsModal", { enumerable: true, get: function () { return integrations_modal_js_1.ConnectIntegrationsModal; } });
|
|
82
108
|
var plans_js_1 = require("./components/plans.js");
|
|
@@ -104,6 +130,8 @@ Object.defineProperty(exports, "useConnectAccessGate", { enumerable: true, get:
|
|
|
104
130
|
var feature_gate_js_1 = require("./components/feature-gate.js");
|
|
105
131
|
Object.defineProperty(exports, "ConnectFeatureGate", { enumerable: true, get: function () { return feature_gate_js_1.ConnectFeatureGate; } });
|
|
106
132
|
Object.defineProperty(exports, "useConnectFeature", { enumerable: true, get: function () { return feature_gate_js_1.useConnectFeature; } });
|
|
133
|
+
var workspace_type_gate_js_1 = require("./components/workspace-type-gate.js");
|
|
134
|
+
Object.defineProperty(exports, "ConnectWorkspaceTypeGate", { enumerable: true, get: function () { return workspace_type_gate_js_1.ConnectWorkspaceTypeGate; } });
|
|
107
135
|
var usage_meter_js_1 = require("./components/usage-meter.js");
|
|
108
136
|
Object.defineProperty(exports, "ConnectUsageMeter", { enumerable: true, get: function () { return usage_meter_js_1.ConnectUsageMeter; } });
|
|
109
137
|
var plans_page_js_1 = require("./components/plans-page.js");
|
package/dist/cjs/client/mark.js
CHANGED
|
@@ -7,7 +7,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
7
7
|
* The Connect mark - concentric rings on a solid accent badge.
|
|
8
8
|
*
|
|
9
9
|
* Painted from `--accent` / `--accent-foreground`, the same HeroUI tokens the
|
|
10
|
-
* host app themes, so the badge tracks whatever palette
|
|
10
|
+
* host app themes, so the badge tracks whatever palette an application platform has
|
|
11
11
|
* configured rather than carrying a hardcoded CosmoOps green into someone
|
|
12
12
|
* else's brand.
|
|
13
13
|
*/
|
|
@@ -5,6 +5,8 @@ exports.CONNECT_APP_URL = void 0;
|
|
|
5
5
|
exports.ConnectProvider = ConnectProvider;
|
|
6
6
|
exports.useConnect = useConnect;
|
|
7
7
|
exports.useConnectUser = useConnectUser;
|
|
8
|
+
exports.useConnectWorkspaceType = useConnectWorkspaceType;
|
|
9
|
+
exports.useConnectAudience = useConnectAudience;
|
|
8
10
|
exports.useConnectEntitlements = useConnectEntitlements;
|
|
9
11
|
exports.useConnectAddonUsage = useConnectAddonUsage;
|
|
10
12
|
exports.useConnectPlans = useConnectPlans;
|
|
@@ -124,6 +126,20 @@ function ConnectProvider({ clientId = "", basePath = "/api/connect", redirectUri
|
|
|
124
126
|
const invalidateResource = (0, react_1.useCallback)((path) => {
|
|
125
127
|
cache.current.delete(`${basePath}${path}`);
|
|
126
128
|
}, [basePath]);
|
|
129
|
+
const streamResource = (0, react_1.useCallback)(async (path, body, options) => {
|
|
130
|
+
const response = await (fetchImpl ?? globalThis.fetch)(`${basePath}${path}`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
credentials: "same-origin",
|
|
133
|
+
headers: { accept: "text/event-stream", "content-type": "application/json" },
|
|
134
|
+
body: JSON.stringify(body),
|
|
135
|
+
signal: options?.signal,
|
|
136
|
+
});
|
|
137
|
+
if (!response.ok) {
|
|
138
|
+
const envelope = await response.json().catch(() => null);
|
|
139
|
+
throw new Error(envelope?.error?.message ?? `Connect bridge returned ${response.status}`);
|
|
140
|
+
}
|
|
141
|
+
return response;
|
|
142
|
+
}, [basePath, fetchImpl]);
|
|
127
143
|
const refresh = (0, react_1.useCallback)(async () => {
|
|
128
144
|
cache.current.clear();
|
|
129
145
|
try {
|
|
@@ -135,7 +151,7 @@ function ConnectProvider({ clientId = "", basePath = "/api/connect", redirectUri
|
|
|
135
151
|
// error, so a Login button still renders rather than the UI hanging on
|
|
136
152
|
// skeletons forever.
|
|
137
153
|
setResult({
|
|
138
|
-
session: { user: null, subscription: null,
|
|
154
|
+
session: { user: null, subscription: null, applicationRole: null, audience: null },
|
|
139
155
|
error: cause instanceof Error ? cause.message : "Could not reach Connect",
|
|
140
156
|
});
|
|
141
157
|
}
|
|
@@ -184,7 +200,7 @@ function ConnectProvider({ clientId = "", basePath = "/api/connect", redirectUri
|
|
|
184
200
|
credentials: "same-origin",
|
|
185
201
|
}).catch(() => { });
|
|
186
202
|
cache.current.clear();
|
|
187
|
-
setResult({ session: { user: null, subscription: null,
|
|
203
|
+
setResult({ session: { user: null, subscription: null, applicationRole: null, audience: null }, error: null });
|
|
188
204
|
}, [basePath, fetchImpl]);
|
|
189
205
|
const value = (0, react_1.useMemo)(() => ({
|
|
190
206
|
clientId,
|
|
@@ -198,6 +214,7 @@ function ConnectProvider({ clientId = "", basePath = "/api/connect", redirectUri
|
|
|
198
214
|
fetchResource,
|
|
199
215
|
mutateResource,
|
|
200
216
|
invalidateResource,
|
|
217
|
+
streamResource,
|
|
201
218
|
}), [
|
|
202
219
|
clientId,
|
|
203
220
|
appUrl,
|
|
@@ -210,6 +227,7 @@ function ConnectProvider({ clientId = "", basePath = "/api/connect", redirectUri
|
|
|
210
227
|
fetchResource,
|
|
211
228
|
mutateResource,
|
|
212
229
|
invalidateResource,
|
|
230
|
+
streamResource,
|
|
213
231
|
]);
|
|
214
232
|
return (0, jsx_runtime_1.jsx)(ConnectContext.Provider, { value: value, children: children });
|
|
215
233
|
}
|
|
@@ -224,6 +242,31 @@ function useConnect() {
|
|
|
224
242
|
function useConnectUser() {
|
|
225
243
|
return useConnect().session?.user ?? null;
|
|
226
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Whether the signed-in workspace is a one-seat personal account or a team -
|
|
247
|
+
* `null` while the session is loading or signed out.
|
|
248
|
+
*
|
|
249
|
+
* Branch on this to stop rendering team UI at a solo account: a members
|
|
250
|
+
* table, an invite button and a seat meter are all dead controls on an
|
|
251
|
+
* `"individual"` workspace, which has exactly one seat and cannot gain
|
|
252
|
+
* another. `<ConnectWorkspaceTypeGate>` is the declarative form.
|
|
253
|
+
*/
|
|
254
|
+
function useConnectWorkspaceType() {
|
|
255
|
+
return useConnect().session?.user?.workspaceType ?? null;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Whether this account is the kind your application is sold to - `null` while
|
|
259
|
+
* the session is loading or signed out.
|
|
260
|
+
*
|
|
261
|
+
* `audience.matches === false` is a hard block, not a hint: readiness reports
|
|
262
|
+
* `audience_mismatch` and subscribing is refused. `<ConnectAccessGate>`
|
|
263
|
+
* already blocks the shell on it, so reach for this only when you need the
|
|
264
|
+
* verdict somewhere the gate does not run - a marketing page, say, or a
|
|
265
|
+
* sign-up CTA you would rather not show to an account that cannot complete it.
|
|
266
|
+
*/
|
|
267
|
+
function useConnectAudience() {
|
|
268
|
+
return useConnect().session?.audience ?? null;
|
|
269
|
+
}
|
|
227
270
|
/** Readiness gates and the live subscription, fetched on first use. */
|
|
228
271
|
function useConnectEntitlements() {
|
|
229
272
|
return useConnectResource("/entitlements");
|
|
@@ -231,28 +274,28 @@ function useConnectEntitlements() {
|
|
|
231
274
|
/**
|
|
232
275
|
* What is left of every consumable add-on on this workspace's subscription.
|
|
233
276
|
*
|
|
234
|
-
* The read half of `client.billing.consumeAddon` -
|
|
277
|
+
* The read half of `client.billing.consumeAddon` - an application that meters its
|
|
235
278
|
* own end users had no way to show them what remained until this existed.
|
|
236
279
|
* Empty `addons` means there is nothing to meter, which is not an error.
|
|
237
280
|
*/
|
|
238
281
|
function useConnectAddonUsage() {
|
|
239
282
|
return useConnectResource("/addons");
|
|
240
283
|
}
|
|
241
|
-
/** This
|
|
284
|
+
/** This application's plan catalog. */
|
|
242
285
|
function useConnectPlans() {
|
|
243
286
|
return useConnectResource("/plans");
|
|
244
287
|
}
|
|
245
|
-
/** The apps this
|
|
288
|
+
/** The apps this workspace can open. */
|
|
246
289
|
function useConnectApps() {
|
|
247
290
|
return useConnectResource("/apps");
|
|
248
291
|
}
|
|
249
|
-
/** Enabled Composio-hosted toolkits and this
|
|
292
|
+
/** Enabled Composio-hosted toolkits and this workspace's connection status for each. */
|
|
250
293
|
function useConnectIntegrations() {
|
|
251
294
|
return useConnectResource("/integrations");
|
|
252
295
|
}
|
|
253
296
|
/**
|
|
254
297
|
* This Connect account's own login sessions - account-wide, not scoped to
|
|
255
|
-
* this
|
|
298
|
+
* this application, see `SessionsListResponse`'s doc comment. The component
|
|
256
299
|
* that calls this is also the one that must own any revoke actions and call
|
|
257
300
|
* the returned `refetch()` afterward - see `mutateResource`'s doc comment
|
|
258
301
|
* for why nothing does that automatically.
|
|
@@ -157,15 +157,15 @@ 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/(.)
|
|
160
|
+
* parallel route - `app/@modal/(.)applications/...` and
|
|
161
161
|
* `app/@modal/(.)billing/...`), so the customer arrives already looking at the
|
|
162
162
|
* confirmation for the plan they picked here, not at a catalogue they have to
|
|
163
163
|
* find it in again.
|
|
164
164
|
*/
|
|
165
|
-
function buildCheckoutUrl({ appUrl,
|
|
165
|
+
function buildCheckoutUrl({ appUrl, applicationId, planId, subscriptionId, coupon, }) {
|
|
166
166
|
const path = subscriptionId
|
|
167
167
|
? `/billing/subscriptions/${encodeURIComponent(subscriptionId)}/change-plan/${encodeURIComponent(planId)}`
|
|
168
|
-
: `/
|
|
168
|
+
: `/applications/${encodeURIComponent(applicationId)}/subscribe/${encodeURIComponent(planId)}`;
|
|
169
169
|
const url = new URL(path, appUrl);
|
|
170
170
|
if (coupon?.trim())
|
|
171
171
|
url.searchParams.set("coupon", coupon.trim());
|
package/dist/cjs/errors.js
CHANGED
|
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ConnectError = void 0;
|
|
4
4
|
exports.isConnectError = isConnectError;
|
|
5
5
|
/**
|
|
6
|
-
* Every failure the Connect
|
|
6
|
+
* Every failure the Connect application API can report, as one class.
|
|
7
7
|
*
|
|
8
8
|
* The API speaks two error dialects and this collapses them: the
|
|
9
|
-
* `/api/v1/
|
|
9
|
+
* `/api/v1/application/*` endpoints use `{ error: { code, message } }`, while the
|
|
10
10
|
* three `/sso/*` ones use OAuth2's `{ error, error_description }` (RFC 6749
|
|
11
11
|
* §5.2). Both land here with `code` populated, so integrating code branches on
|
|
12
12
|
* one field either way.
|
|
@@ -45,7 +45,7 @@ class ConnectError extends Error {
|
|
|
45
45
|
* Whether the user's Connect sign-in is over and they need to go back
|
|
46
46
|
* through `identity.authorizeUrl()`. Collapses every session-side failure,
|
|
47
47
|
* matching how the API reports them - unknown, expired, revoked, or issued
|
|
48
|
-
* to a different
|
|
48
|
+
* to a different application all arrive as one `invalid_token`.
|
|
49
49
|
*/
|
|
50
50
|
get isSessionExpired() {
|
|
51
51
|
return this.code === "invalid_token";
|
|
@@ -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
|
|
62
|
+
* has to be reissued from the application's Integration tab.
|
|
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/helpers.js
CHANGED
|
@@ -16,7 +16,7 @@ function blockingBlockers(readiness) {
|
|
|
16
16
|
* Coerces either wire shape into `PlanFeature[]`.
|
|
17
17
|
*
|
|
18
18
|
* A bare key becomes `{ key, name: key, description: "" }` - the same
|
|
19
|
-
* fallback the server applies for a plan older than its
|
|
19
|
+
* fallback the server applies for a plan older than its application's feature
|
|
20
20
|
* catalog, so an un-upgraded Connect renders exactly what it always did
|
|
21
21
|
* rather than a column of blanks.
|
|
22
22
|
*
|
package/dist/cjs/index.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
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/*`).
|
|
7
7
|
*
|
|
8
8
|
* This root entry is the isomorphic surface: wire types, the error class, and
|
|
9
9
|
* pure helpers. It pulls in neither Node-only code nor React, so it is safe to
|
package/dist/cjs/next/index.js
CHANGED
|
@@ -82,7 +82,7 @@ const handler_js_1 = require("../server/handler.js");
|
|
|
82
82
|
* | `CONNECT_SESSION_COOKIE` | no | session cookie name |
|
|
83
83
|
*
|
|
84
84
|
* Register `{your origin}/api/connect/callback` as a redirect URI in the
|
|
85
|
-
*
|
|
85
|
+
* application's Integration tab. That is the only step this cannot do for you.
|
|
86
86
|
*
|
|
87
87
|
* No `next.config.ts` changes needed either, including `transpilePackages`:
|
|
88
88
|
* `dist/` is already ES2022 with `"use client"` hoisted to the top of every
|
|
@@ -317,7 +317,7 @@ async function getConnectSession(request, options = {}) {
|
|
|
317
317
|
* A thin wrapper, not a framework of its own - reach for `getConnectSession`
|
|
318
318
|
* plus your own check when "not signed in" isn't "redirect to sign-in"
|
|
319
319
|
* (an API route returning 401, say). Role and entitlement checks stay
|
|
320
|
-
* outside this function on purpose: `session.
|
|
320
|
+
* outside this function on purpose: `session.applicationRole.granted` and
|
|
321
321
|
* `connect.entitlements.*` are one line each on top of the session this
|
|
322
322
|
* already gives you, and where that line redirects (a plans page, a
|
|
323
323
|
* "forbidden" screen) is a decision only your app can make - see
|
|
@@ -14,7 +14,7 @@ const analytics_js_1 = require("./resources/analytics.js");
|
|
|
14
14
|
const otp_js_1 = require("./resources/otp.js");
|
|
15
15
|
const sessions_js_1 = require("./resources/sessions.js");
|
|
16
16
|
exports.DEFAULT_BASE_URL = "https://connect.cosmoops.com";
|
|
17
|
-
const
|
|
17
|
+
const APPLICATION_API_PREFIX = "/api/v1/application";
|
|
18
18
|
/**
|
|
19
19
|
* Refuses to build a client in a browser.
|
|
20
20
|
*
|
|
@@ -43,9 +43,9 @@ function assertNotBrowser() {
|
|
|
43
43
|
"in browser code instead; it talks to your own origin and holds no credential.");
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
|
-
* The CosmoOps Connect
|
|
46
|
+
* The CosmoOps Connect application-partner API.
|
|
47
47
|
*
|
|
48
|
-
* Covers `/api/v1/
|
|
48
|
+
* Covers `/api/v1/application/*` and nothing else - the surface an application platform
|
|
49
49
|
* integrates against. Connect's internal API is not part of this package and
|
|
50
50
|
* never will be.
|
|
51
51
|
*
|
|
@@ -56,7 +56,7 @@ function assertNotBrowser() {
|
|
|
56
56
|
* });
|
|
57
57
|
*
|
|
58
58
|
* const session = await connect.identity.exchangeCode({ code, redirectUri });
|
|
59
|
-
* const { ready } = await connect.entitlements.getReadiness(session.user.
|
|
59
|
+
* const { ready } = await connect.entitlements.getReadiness(session.user.workspaceId);
|
|
60
60
|
* ```
|
|
61
61
|
*
|
|
62
62
|
* Every failure throws a `ConnectError` carrying a stable `code`. Nothing here
|
|
@@ -65,27 +65,27 @@ function assertNotBrowser() {
|
|
|
65
65
|
class ConnectClient {
|
|
66
66
|
/** "Login with Connect" - the OAuth2 handshake and the session after it. */
|
|
67
67
|
identity;
|
|
68
|
-
/** What a signed-in member may do on your
|
|
68
|
+
/** What a signed-in member may do on your application. */
|
|
69
69
|
access;
|
|
70
|
-
/** The
|
|
70
|
+
/** The applications a workspace can open - for a launcher grid. */
|
|
71
71
|
apps;
|
|
72
|
-
/** Whether a workspace may use your
|
|
72
|
+
/** Whether a workspace may use your application, and on what plan. */
|
|
73
73
|
entitlements;
|
|
74
74
|
/** Your plan catalog, and metering usage against add-on balances. */
|
|
75
75
|
billing;
|
|
76
|
-
/** Files in
|
|
76
|
+
/** Files in a workspace's Drive. */
|
|
77
77
|
drive;
|
|
78
78
|
/** RAG chat and structured extraction. */
|
|
79
79
|
ai;
|
|
80
80
|
/** WhatsApp/SMS phone verification. */
|
|
81
81
|
otp;
|
|
82
|
-
/** Feature events into the Console's audit log, filterable to your
|
|
82
|
+
/** Feature events into the Console's audit log, filterable to your application. */
|
|
83
83
|
logs;
|
|
84
|
-
/** Emit product events and read back this
|
|
84
|
+
/** Emit product events and read back this application's aggregated numbers. */
|
|
85
85
|
analytics;
|
|
86
|
-
/** This Connect account's own login sessions - account-wide, not scoped to this
|
|
86
|
+
/** This Connect account's own login sessions - account-wide, not scoped to this application. Requires the `sessions:manage` scope. */
|
|
87
87
|
sessions;
|
|
88
|
-
/** Composio-hosted third-party connections (Google Calendar, Slack, ...)
|
|
88
|
+
/** Composio-hosted third-party connections (Google Calendar, Slack, ...) a workspace has connected. */
|
|
89
89
|
integrations;
|
|
90
90
|
constructor(options) {
|
|
91
91
|
assertNotBrowser();
|
|
@@ -94,7 +94,7 @@ class ConnectClient {
|
|
|
94
94
|
}
|
|
95
95
|
const appBaseUrl = (options.baseUrl ?? exports.DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
96
96
|
const transport = {
|
|
97
|
-
baseUrl: `${appBaseUrl}${
|
|
97
|
+
baseUrl: `${appBaseUrl}${APPLICATION_API_PREFIX}`,
|
|
98
98
|
apiKey: options.apiKey,
|
|
99
99
|
timeoutMs: options.timeoutMs ?? 20_000,
|
|
100
100
|
maxRetries: options.maxRetries ?? 2,
|
|
@@ -102,7 +102,7 @@ class ConnectClient {
|
|
|
102
102
|
userAgent: `cosmoops-connect-sdk/${exports.SDK_VERSION}`,
|
|
103
103
|
};
|
|
104
104
|
// `authorizeUrl` points at the app, not the API prefix - it is a browser
|
|
105
|
-
// redirect, not
|
|
105
|
+
// redirect, not an application call, so identity takes both.
|
|
106
106
|
this.identity = new identity_js_1.IdentityResource(transport, options.clientId ?? null, appBaseUrl);
|
|
107
107
|
this.access = new access_js_1.AccessResource(transport);
|
|
108
108
|
this.apps = new apps_js_1.AppsResource(transport);
|
|
@@ -119,4 +119,4 @@ class ConnectClient {
|
|
|
119
119
|
}
|
|
120
120
|
exports.ConnectClient = ConnectClient;
|
|
121
121
|
/** Kept in step with `package.json` by `sdk.test.ts`. */
|
|
122
|
-
exports.SDK_VERSION = "0.
|
|
122
|
+
exports.SDK_VERSION = "0.9.0";
|