@cosmoops/connect-sdk 0.23.0 → 0.24.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 +37 -0
- package/dist/cjs/server/client.js +1 -1
- package/dist/cjs/server/handler.js +12 -2
- package/dist/cjs/server/resources/integrations.js +7 -5
- package/dist/esm/server/client.js +1 -1
- package/dist/esm/server/handler.js +12 -2
- package/dist/esm/server/resources/integrations.js +7 -5
- package/dist/types/server/client.d.ts +1 -1
- package/dist/types/server/index.d.ts +1 -1
- package/dist/types/server/resources/integrations.d.ts +20 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,43 @@
|
|
|
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.0
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **`connect.integrations.initiateConnect()` could never succeed.** Connect's
|
|
11
|
+
`POST /integrations/connect` requires a `userId` - it stores it as the
|
|
12
|
+
connection's `connectedByUserId` - and the SDK sent `workspaceId`,
|
|
13
|
+
`toolkitSlug` and `returnUrl` and nothing else. Every call 400'd, including
|
|
14
|
+
the one `createConnectHandler`'s own `POST /integrations/connect` route
|
|
15
|
+
makes, so **`<ConnectIntegrationsModal />`'s Connect button was broken too**:
|
|
16
|
+
the browser reached the bridge, the bridge reached Connect, and Connect
|
|
17
|
+
refused with `Invalid input: expected string, received undefined` - a Zod
|
|
18
|
+
message that names no field, which is most of why this survived.
|
|
19
|
+
|
|
20
|
+
The conformance suite pinned the *response* shape and not the request, so
|
|
21
|
+
nothing failed the build. It now pins the request body against the
|
|
22
|
+
use-case command, and `src/conformance.test.ts` fails to compile if the
|
|
23
|
+
route grows a fourth required field.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **`initiateConnect` takes an input object** - `initiateConnect(workspaceId,
|
|
28
|
+
{ toolkitSlug, returnUrl, userId })`, replacing the three positional
|
|
29
|
+
strings. Breaking in name only: the old signature had no working call to
|
|
30
|
+
break. The object shape matches `execute()` on the same resource, and it is
|
|
31
|
+
what keeps `returnUrl` and `userId` from being silently transposable.
|
|
32
|
+
|
|
33
|
+
`userId` is the signed-in member starting the connection. Take it from your
|
|
34
|
+
own session, never from a form: the toolkit connection is shared by the
|
|
35
|
+
whole workspace, so this attribution is the only record of who turned it on.
|
|
36
|
+
The bridge already does this for you - it reads the session's user, and a
|
|
37
|
+
`userId` in the request body is ignored, the same way a crafted
|
|
38
|
+
`workspaceId` always was.
|
|
39
|
+
|
|
40
|
+
- **`InitiateIntegrationConnectionInput` is exported** from
|
|
41
|
+
`@cosmoops/connect-sdk/server`, beside `ExecuteIntegrationActionInput`.
|
|
42
|
+
|
|
6
43
|
## 0.23.0
|
|
7
44
|
|
|
8
45
|
### Added
|
|
@@ -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,
|
|
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
|
|
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,
|
|
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,
|
|
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. */
|
|
@@ -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,
|
|
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
|
|
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,
|
|
41
|
+
async initiateConnect(workspaceId, input) {
|
|
40
42
|
return request(this.transport, {
|
|
41
43
|
method: "POST",
|
|
42
44
|
path: "/integrations/connect",
|
|
43
|
-
json: { workspaceId,
|
|
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. */
|
|
@@ -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
|
|
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,
|
|
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.
|
|
3
|
+
"version": "0.24.0",
|
|
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",
|