@cosmoops/connect-sdk 0.23.0 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,67 @@
3
3
  `@cosmoops/connect-sdk` follows semver, with the 0.x convention that **minor
4
4
  versions may carry breaking changes** and patch versions never do.
5
5
 
6
+ ## 0.24.1
7
+
8
+ ### Fixed
9
+
10
+ - **`<ConnectConfirmDialog />` never opened.** Its trigger was wrapped in
11
+ `Modal.Trigger`, which renders a `div role="button"` inside React Aria's
12
+ `Pressable` and depends on a plain `onClick` bubbling up to it. A React Aria
13
+ button nested inside - which every real trigger is, including HeroUI's
14
+ `<Button>` - calls `e.stopPropagation()` in its own `usePress` click
15
+ handler, so the click never arrived. Nothing threw and nothing warned; the
16
+ press was simply eaten, and every confirmation in every app built on this
17
+ SDK was inert. Twelve of them across the two reference apps: each delete
18
+ confirmation in the CRM, and each in the club app.
19
+
20
+ The trigger is now a direct child of the `Modal` root, which is React Aria's
21
+ `DialogTrigger`. Its `PressResponder` picks the trigger up directly, so
22
+ there is no nested pressable and no propagation to lose.
23
+
24
+ No call site changes. The prop's contract is now stated the right way round:
25
+ the trigger **must** be a pressable - a HeroUI `Button`, or anything else
26
+ built on React Aria's `useButton`. A non-pressable element (a bare `<span>`)
27
+ used to be the only thing that worked and now does not; nothing shipped
28
+ passes one.
29
+
30
+ ## 0.24.0
31
+
32
+ ### Fixed
33
+
34
+ - **`connect.integrations.initiateConnect()` could never succeed.** Connect's
35
+ `POST /integrations/connect` requires a `userId` - it stores it as the
36
+ connection's `connectedByUserId` - and the SDK sent `workspaceId`,
37
+ `toolkitSlug` and `returnUrl` and nothing else. Every call 400'd, including
38
+ the one `createConnectHandler`'s own `POST /integrations/connect` route
39
+ makes, so **`<ConnectIntegrationsModal />`'s Connect button was broken too**:
40
+ the browser reached the bridge, the bridge reached Connect, and Connect
41
+ refused with `Invalid input: expected string, received undefined` - a Zod
42
+ message that names no field, which is most of why this survived.
43
+
44
+ The conformance suite pinned the *response* shape and not the request, so
45
+ nothing failed the build. It now pins the request body against the
46
+ use-case command, and `src/conformance.test.ts` fails to compile if the
47
+ route grows a fourth required field.
48
+
49
+ ### Changed
50
+
51
+ - **`initiateConnect` takes an input object** - `initiateConnect(workspaceId,
52
+ { toolkitSlug, returnUrl, userId })`, replacing the three positional
53
+ strings. Breaking in name only: the old signature had no working call to
54
+ break. The object shape matches `execute()` on the same resource, and it is
55
+ what keeps `returnUrl` and `userId` from being silently transposable.
56
+
57
+ `userId` is the signed-in member starting the connection. Take it from your
58
+ own session, never from a form: the toolkit connection is shared by the
59
+ whole workspace, so this attribution is the only record of who turned it on.
60
+ The bridge already does this for you - it reads the session's user, and a
61
+ `userId` in the request body is ignored, the same way a crafted
62
+ `workspaceId` always was.
63
+
64
+ - **`InitiateIntegrationConnectionInput` is exported** from
65
+ `@cosmoops/connect-sdk/server`, beside `ExecuteIntegrationActionInput`.
66
+
6
67
  ## 0.23.0
7
68
 
8
69
  ### Added
@@ -19,11 +19,19 @@ const react_2 = require("@heroui/react");
19
19
  * rejection is caught here rather than escaping - an error thrown out of an
20
20
  * async transition reaches the nearest error boundary and takes the page down.
21
21
  *
22
- * The anatomy is HeroUI's: `Modal.Backdrop` owns the open state,
23
- * `Modal.Container` the sizing, `Modal.Dialog` the focus trap. The trigger
24
- * goes inside `Modal.Trigger`, which *is* a React Aria button - so pass a
25
- * plain element, never a `<Button>`, or the nested button swallows the press
26
- * and the dialog silently never opens.
22
+ * The anatomy is HeroUI's: the `Modal` root owns the open state,
23
+ * `Modal.Container` the sizing, `Modal.Dialog` the focus trap. **The trigger
24
+ * is a direct child of the root**, never wrapped in `Modal.Trigger`.
25
+ *
26
+ * That wrapping is what this component used to do, and it meant the dialog
27
+ * never opened for any caller passing a `<Button>` - which was all of them.
28
+ * `Modal.Trigger` renders a `div role="button"` inside React Aria's
29
+ * `Pressable` and relies on a plain `onClick` bubbling up to it; a React Aria
30
+ * button nested inside calls `e.stopPropagation()` in its own `usePress`
31
+ * click handler, so the click never arrives. Nothing failed loudly - the
32
+ * press was simply eaten. The root is `DialogTrigger`, whose `PressResponder`
33
+ * the trigger registers with directly, so there is no nesting and no
34
+ * propagation to lose.
27
35
  */
28
36
  function ConnectConfirmDialog({ trigger, title, description, confirmLabel = "Confirm", cancelLabel = "Cancel", tone = "danger", onConfirm, }) {
29
37
  const [isOpen, setIsOpen] = (0, react_1.useState)(false);
@@ -36,5 +44,5 @@ function ConnectConfirmDialog({ trigger, title, description, confirmLabel = "Con
36
44
  // caller, which owns the toast and the rollback.
37
45
  });
38
46
  };
39
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Modal.Trigger, { onClick: () => setIsOpen(true), children: trigger }), (0, jsx_runtime_1.jsx)(react_2.Modal.Backdrop, { isOpen: isOpen, onOpenChange: setIsOpen, children: (0, jsx_runtime_1.jsx)(react_2.Modal.Container, { size: "sm", children: (0, jsx_runtime_1.jsxs)(react_2.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_2.Modal.CloseTrigger, { onPress: () => setIsOpen(false) }), (0, jsx_runtime_1.jsx)(react_2.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_2.Modal.Heading, { children: title }) }), description && ((0, jsx_runtime_1.jsx)(react_2.Modal.Body, { children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: description }) })), (0, jsx_runtime_1.jsxs)(react_2.Modal.Footer, { children: [(0, jsx_runtime_1.jsx)(react_2.Button, { variant: "secondary", onPress: () => setIsOpen(false), children: cancelLabel }), (0, jsx_runtime_1.jsx)(react_2.Button, { variant: tone === "danger" ? "danger" : "primary", onPress: confirm, children: confirmLabel })] })] }) }) })] }));
47
+ return ((0, jsx_runtime_1.jsxs)(react_2.Modal, { isOpen: isOpen, onOpenChange: setIsOpen, children: [trigger, (0, jsx_runtime_1.jsx)(react_2.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_2.Modal.Container, { size: "sm", children: (0, jsx_runtime_1.jsxs)(react_2.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_2.Modal.CloseTrigger, { onPress: () => setIsOpen(false) }), (0, jsx_runtime_1.jsx)(react_2.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_2.Modal.Heading, { children: title }) }), description && ((0, jsx_runtime_1.jsx)(react_2.Modal.Body, { children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: description }) })), (0, jsx_runtime_1.jsxs)(react_2.Modal.Footer, { children: [(0, jsx_runtime_1.jsx)(react_2.Button, { variant: "secondary", onPress: () => setIsOpen(false), children: cancelLabel }), (0, jsx_runtime_1.jsx)(react_2.Button, { variant: tone === "danger" ? "danger" : "primary", onPress: confirm, children: confirmLabel })] })] }) }) })] }));
40
48
  }
@@ -161,4 +161,4 @@ class ConnectClient {
161
161
  }
162
162
  exports.ConnectClient = ConnectClient;
163
163
  /** Kept in step with `package.json` by `sdk.test.ts`. */
164
- exports.SDK_VERSION = "0.23.0";
164
+ exports.SDK_VERSION = "0.24.1";
@@ -605,8 +605,14 @@ function createConnectHandler(options) {
605
605
  const { workspaceId } = await requireWorkspace(request);
606
606
  return json({ toolkits: await client.integrations.list(workspaceId) });
607
607
  },
608
+ /*
609
+ * `userId` comes from the session alongside `workspaceId`, never from the
610
+ * body - it is stored as `connectedByUserId`, so letting the browser name
611
+ * it would let anyone sign a workspace-shared connection with someone
612
+ * else's name.
613
+ */
608
614
  "POST /integrations/connect": async (request) => {
609
- const { workspaceId } = await requireWorkspace(request);
615
+ const { workspaceId, user } = await requireWorkspace(request);
610
616
  const body = (await request.json().catch(() => null));
611
617
  if (!body?.toolkitSlug || !body?.returnUrl) {
612
618
  throw new errors_js_1.ConnectError({
@@ -616,7 +622,11 @@ function createConnectHandler(options) {
616
622
  endpoint: "POST /integrations/connect",
617
623
  });
618
624
  }
619
- return json(await client.integrations.initiateConnect(workspaceId, body.toolkitSlug, body.returnUrl));
625
+ return json(await client.integrations.initiateConnect(workspaceId, {
626
+ toolkitSlug: body.toolkitSlug,
627
+ returnUrl: body.returnUrl,
628
+ userId: user.userId,
629
+ }));
620
630
  },
621
631
  "POST /integrations/disconnect": async (request) => {
622
632
  const { workspaceId } = await requireWorkspace(request);
@@ -35,15 +35,17 @@ class IntegrationsResource {
35
35
  * itself. Reconnecting a disconnected/failed toolkit calls this same
36
36
  * method - there is no separate reconnect.
37
37
  *
38
- * Meant to be called on behalf of a signed-in workspace admin (this is what the
39
- * bridge's fixed `POST /integrations/connect` route does), not as a bare
40
- * background action with no admin present to complete the OAuth screen.
38
+ * Meant to be called on behalf of a signed-in workspace admin (this is what
39
+ * the bridge's fixed `POST /integrations/connect` route does), not as a bare
40
+ * background action with no admin present to complete the OAuth screen. That
41
+ * is why `userId` is part of the input rather than inferred - see
42
+ * `InitiateIntegrationConnectionInput`.
41
43
  */
42
- async initiateConnect(workspaceId, toolkitSlug, returnUrl) {
44
+ async initiateConnect(workspaceId, input) {
43
45
  return (0, http_js_1.request)(this.transport, {
44
46
  method: "POST",
45
47
  path: "/integrations/connect",
46
- json: { workspaceId, toolkitSlug, returnUrl },
48
+ json: { workspaceId, ...input },
47
49
  });
48
50
  }
49
51
  /** Idempotent - disconnecting a toolkit that was never connected, or is already disconnected, is a success. */
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Button, Modal } from "@heroui/react";
5
5
  /**
@@ -16,11 +16,19 @@ import { Button, Modal } from "@heroui/react";
16
16
  * rejection is caught here rather than escaping - an error thrown out of an
17
17
  * async transition reaches the nearest error boundary and takes the page down.
18
18
  *
19
- * The anatomy is HeroUI's: `Modal.Backdrop` owns the open state,
20
- * `Modal.Container` the sizing, `Modal.Dialog` the focus trap. The trigger
21
- * goes inside `Modal.Trigger`, which *is* a React Aria button - so pass a
22
- * plain element, never a `<Button>`, or the nested button swallows the press
23
- * and the dialog silently never opens.
19
+ * The anatomy is HeroUI's: the `Modal` root owns the open state,
20
+ * `Modal.Container` the sizing, `Modal.Dialog` the focus trap. **The trigger
21
+ * is a direct child of the root**, never wrapped in `Modal.Trigger`.
22
+ *
23
+ * That wrapping is what this component used to do, and it meant the dialog
24
+ * never opened for any caller passing a `<Button>` - which was all of them.
25
+ * `Modal.Trigger` renders a `div role="button"` inside React Aria's
26
+ * `Pressable` and relies on a plain `onClick` bubbling up to it; a React Aria
27
+ * button nested inside calls `e.stopPropagation()` in its own `usePress`
28
+ * click handler, so the click never arrives. Nothing failed loudly - the
29
+ * press was simply eaten. The root is `DialogTrigger`, whose `PressResponder`
30
+ * the trigger registers with directly, so there is no nesting and no
31
+ * propagation to lose.
24
32
  */
25
33
  export function ConnectConfirmDialog({ trigger, title, description, confirmLabel = "Confirm", cancelLabel = "Cancel", tone = "danger", onConfirm, }) {
26
34
  const [isOpen, setIsOpen] = useState(false);
@@ -33,5 +41,5 @@ export function ConnectConfirmDialog({ trigger, title, description, confirmLabel
33
41
  // caller, which owns the toast and the rollback.
34
42
  });
35
43
  };
36
- return (_jsxs(_Fragment, { children: [_jsx(Modal.Trigger, { onClick: () => setIsOpen(true), children: trigger }), _jsx(Modal.Backdrop, { isOpen: isOpen, onOpenChange: setIsOpen, children: _jsx(Modal.Container, { size: "sm", children: _jsxs(Modal.Dialog, { children: [_jsx(Modal.CloseTrigger, { onPress: () => setIsOpen(false) }), _jsx(Modal.Header, { children: _jsx(Modal.Heading, { children: title }) }), description && (_jsx(Modal.Body, { children: _jsx("p", { className: "text-sm text-muted", children: description }) })), _jsxs(Modal.Footer, { children: [_jsx(Button, { variant: "secondary", onPress: () => setIsOpen(false), children: cancelLabel }), _jsx(Button, { variant: tone === "danger" ? "danger" : "primary", onPress: confirm, children: confirmLabel })] })] }) }) })] }));
44
+ return (_jsxs(Modal, { isOpen: isOpen, onOpenChange: setIsOpen, children: [trigger, _jsx(Modal.Backdrop, { children: _jsx(Modal.Container, { size: "sm", children: _jsxs(Modal.Dialog, { children: [_jsx(Modal.CloseTrigger, { onPress: () => setIsOpen(false) }), _jsx(Modal.Header, { children: _jsx(Modal.Heading, { children: title }) }), description && (_jsx(Modal.Body, { children: _jsx("p", { className: "text-sm text-muted", children: description }) })), _jsxs(Modal.Footer, { children: [_jsx(Button, { variant: "secondary", onPress: () => setIsOpen(false), children: cancelLabel }), _jsx(Button, { variant: tone === "danger" ? "danger" : "primary", onPress: confirm, children: confirmLabel })] })] }) }) })] }));
37
45
  }
@@ -157,4 +157,4 @@ export class ConnectClient {
157
157
  }
158
158
  }
159
159
  /** Kept in step with `package.json` by `sdk.test.ts`. */
160
- export const SDK_VERSION = "0.23.0";
160
+ export const SDK_VERSION = "0.24.1";
@@ -602,8 +602,14 @@ export function createConnectHandler(options) {
602
602
  const { workspaceId } = await requireWorkspace(request);
603
603
  return json({ toolkits: await client.integrations.list(workspaceId) });
604
604
  },
605
+ /*
606
+ * `userId` comes from the session alongside `workspaceId`, never from the
607
+ * body - it is stored as `connectedByUserId`, so letting the browser name
608
+ * it would let anyone sign a workspace-shared connection with someone
609
+ * else's name.
610
+ */
605
611
  "POST /integrations/connect": async (request) => {
606
- const { workspaceId } = await requireWorkspace(request);
612
+ const { workspaceId, user } = await requireWorkspace(request);
607
613
  const body = (await request.json().catch(() => null));
608
614
  if (!body?.toolkitSlug || !body?.returnUrl) {
609
615
  throw new ConnectError({
@@ -613,7 +619,11 @@ export function createConnectHandler(options) {
613
619
  endpoint: "POST /integrations/connect",
614
620
  });
615
621
  }
616
- return json(await client.integrations.initiateConnect(workspaceId, body.toolkitSlug, body.returnUrl));
622
+ return json(await client.integrations.initiateConnect(workspaceId, {
623
+ toolkitSlug: body.toolkitSlug,
624
+ returnUrl: body.returnUrl,
625
+ userId: user.userId,
626
+ }));
617
627
  },
618
628
  "POST /integrations/disconnect": async (request) => {
619
629
  const { workspaceId } = await requireWorkspace(request);
@@ -32,15 +32,17 @@ export class IntegrationsResource {
32
32
  * itself. Reconnecting a disconnected/failed toolkit calls this same
33
33
  * method - there is no separate reconnect.
34
34
  *
35
- * Meant to be called on behalf of a signed-in workspace admin (this is what the
36
- * bridge's fixed `POST /integrations/connect` route does), not as a bare
37
- * background action with no admin present to complete the OAuth screen.
35
+ * Meant to be called on behalf of a signed-in workspace admin (this is what
36
+ * the bridge's fixed `POST /integrations/connect` route does), not as a bare
37
+ * background action with no admin present to complete the OAuth screen. That
38
+ * is why `userId` is part of the input rather than inferred - see
39
+ * `InitiateIntegrationConnectionInput`.
38
40
  */
39
- async initiateConnect(workspaceId, toolkitSlug, returnUrl) {
41
+ async initiateConnect(workspaceId, input) {
40
42
  return request(this.transport, {
41
43
  method: "POST",
42
44
  path: "/integrations/connect",
43
- json: { workspaceId, toolkitSlug, returnUrl },
45
+ json: { workspaceId, ...input },
44
46
  });
45
47
  }
46
48
  /** Idempotent - disconnecting a toolkit that was never connected, or is already disconnected, is a success. */
@@ -1,6 +1,13 @@
1
1
  import { type ReactNode } from "react";
2
2
  export interface ConnectConfirmDialogProps {
3
- /** The control that opens it. Rendered inside `Modal.Trigger`. */
3
+ /**
4
+ * The control that opens it, and it **must be a pressable** - a HeroUI
5
+ * `Button`, or anything else built on React Aria's `useButton`. It is
6
+ * rendered as a direct child of the `Modal` root, which is React Aria's
7
+ * `DialogTrigger`: that puts a `PressResponder` around its children and the
8
+ * trigger registers with it. A plain `<span>` never registers, and React
9
+ * Aria warns that the responder has no pressable child.
10
+ */
4
11
  trigger: ReactNode;
5
12
  title: ReactNode;
6
13
  /** What will happen, in the user's words. Name the thing being acted on. */
@@ -25,10 +32,18 @@ export interface ConnectConfirmDialogProps {
25
32
  * rejection is caught here rather than escaping - an error thrown out of an
26
33
  * async transition reaches the nearest error boundary and takes the page down.
27
34
  *
28
- * The anatomy is HeroUI's: `Modal.Backdrop` owns the open state,
29
- * `Modal.Container` the sizing, `Modal.Dialog` the focus trap. The trigger
30
- * goes inside `Modal.Trigger`, which *is* a React Aria button - so pass a
31
- * plain element, never a `<Button>`, or the nested button swallows the press
32
- * and the dialog silently never opens.
35
+ * The anatomy is HeroUI's: the `Modal` root owns the open state,
36
+ * `Modal.Container` the sizing, `Modal.Dialog` the focus trap. **The trigger
37
+ * is a direct child of the root**, never wrapped in `Modal.Trigger`.
38
+ *
39
+ * That wrapping is what this component used to do, and it meant the dialog
40
+ * never opened for any caller passing a `<Button>` - which was all of them.
41
+ * `Modal.Trigger` renders a `div role="button"` inside React Aria's
42
+ * `Pressable` and relies on a plain `onClick` bubbling up to it; a React Aria
43
+ * button nested inside calls `e.stopPropagation()` in its own `usePress`
44
+ * click handler, so the click never arrives. Nothing failed loudly - the
45
+ * press was simply eaten. The root is `DialogTrigger`, whose `PressResponder`
46
+ * the trigger registers with directly, so there is no nesting and no
47
+ * propagation to lose.
33
48
  */
34
49
  export declare function ConnectConfirmDialog({ trigger, title, description, confirmLabel, cancelLabel, tone, onConfirm, }: Readonly<ConnectConfirmDialogProps>): import("react").JSX.Element;
@@ -123,4 +123,4 @@ export declare class ConnectClient {
123
123
  constructor(options: ConnectClientOptions);
124
124
  }
125
125
  /** Kept in step with `package.json` by `sdk.test.ts`. */
126
- export declare const SDK_VERSION = "0.23.0";
126
+ export declare const SDK_VERSION = "0.24.1";
@@ -26,7 +26,7 @@ export type { OtpResource } from "./resources/otp.js";
26
26
  export type { LogsResource } from "./resources/logs.js";
27
27
  export type { AnalyticsResource } from "./resources/analytics.js";
28
28
  export type { SessionsResource } from "./resources/sessions.js";
29
- export type { ExecuteIntegrationActionInput, IntegrationsResource } from "./resources/integrations.js";
29
+ export type { ExecuteIntegrationActionInput, InitiateIntegrationConnectionInput, IntegrationsResource, } from "./resources/integrations.js";
30
30
  export type { PlatformResource } from "./resources/platform.js";
31
31
  export type { SchedulingResource, FindSlotsInput, ListBookingsInput, CreateBookingInput, RescheduleBookingInput, } from "./resources/scheduling.js";
32
32
  export type { ContactsResource, LookupContactInput } from "./resources/contacts.js";
@@ -1,5 +1,19 @@
1
1
  import { type TransportOptions } from "../http.js";
2
2
  import type { DisconnectIntegrationResponse, ExecuteIntegrationActionResponse, InitiateIntegrationConnectionResponse, IntegrationsListResponse } from "../../types.js";
3
+ export interface InitiateIntegrationConnectionInput {
4
+ toolkitSlug: string;
5
+ returnUrl: string;
6
+ /**
7
+ * The signed-in member starting the connection, recorded by Connect as
8
+ * `connectedByUserId`.
9
+ *
10
+ * Required, and deliberately not defaulted: the connection is *shared by
11
+ * the whole workspace*, so "who turned this on" is the only attribution
12
+ * anyone gets - and a call that cannot name a person is one where nobody
13
+ * is present to finish the OAuth screen either.
14
+ */
15
+ userId: string;
16
+ }
3
17
  export interface ExecuteIntegrationActionInput {
4
18
  toolkitSlug: string;
5
19
  actionSlug: string;
@@ -28,11 +42,13 @@ export declare class IntegrationsResource {
28
42
  * itself. Reconnecting a disconnected/failed toolkit calls this same
29
43
  * method - there is no separate reconnect.
30
44
  *
31
- * Meant to be called on behalf of a signed-in workspace admin (this is what the
32
- * bridge's fixed `POST /integrations/connect` route does), not as a bare
33
- * background action with no admin present to complete the OAuth screen.
45
+ * Meant to be called on behalf of a signed-in workspace admin (this is what
46
+ * the bridge's fixed `POST /integrations/connect` route does), not as a bare
47
+ * background action with no admin present to complete the OAuth screen. That
48
+ * is why `userId` is part of the input rather than inferred - see
49
+ * `InitiateIntegrationConnectionInput`.
34
50
  */
35
- initiateConnect(workspaceId: string, toolkitSlug: string, returnUrl: string): Promise<InitiateIntegrationConnectionResponse>;
51
+ initiateConnect(workspaceId: string, input: InitiateIntegrationConnectionInput): Promise<InitiateIntegrationConnectionResponse>;
36
52
  /** Idempotent - disconnecting a toolkit that was never connected, or is already disconnected, is a success. */
37
53
  disconnect(workspaceId: string, toolkitSlug: string): Promise<DisconnectIntegrationResponse>;
38
54
  /** Calls a Composio action against this workspace's connected account for the named toolkit. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmoops/connect-sdk",
3
- "version": "0.23.0",
3
+ "version": "0.24.1",
4
4
  "description": "Official server-side SDK for the CosmoOps Connect platform API - Login with Connect, entitlements, billing and metering.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "commonjs",