@doist/comms-sdk 2.0.2 → 3.0.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/dist/esm/utils/url-helpers.js +104 -16
- package/dist/types/utils/url-helpers.d.ts +41 -0
- package/package.json +5 -10
- package/dist/cjs/authentication.js +0 -256
- package/dist/cjs/clients/add-comment-helper.js +0 -43
- package/dist/cjs/clients/attachments-client.js +0 -71
- package/dist/cjs/clients/base-client.js +0 -33
- package/dist/cjs/clients/channels-client.js +0 -211
- package/dist/cjs/clients/comments-client.js +0 -175
- package/dist/cjs/clients/conversation-messages-client.js +0 -169
- package/dist/cjs/clients/conversations-client.js +0 -283
- package/dist/cjs/clients/groups-client.js +0 -172
- package/dist/cjs/clients/hooks-client.js +0 -52
- package/dist/cjs/clients/inbox-client.js +0 -184
- package/dist/cjs/clients/notify-audience.js +0 -45
- package/dist/cjs/clients/reactions-client.js +0 -97
- package/dist/cjs/clients/search-client.js +0 -138
- package/dist/cjs/clients/threads-client.js +0 -364
- package/dist/cjs/clients/users-client.js +0 -326
- package/dist/cjs/clients/workspace-users-client.js +0 -252
- package/dist/cjs/clients/workspaces-client.js +0 -174
- package/dist/cjs/comms-api.js +0 -70
- package/dist/cjs/consts/endpoints.js +0 -34
- package/dist/cjs/index.js +0 -53
- package/dist/cjs/migration.js +0 -96
- package/dist/cjs/package.json +0 -1
- package/dist/cjs/testUtils/msw-handlers.js +0 -51
- package/dist/cjs/testUtils/msw-setup.js +0 -35
- package/dist/cjs/testUtils/obsidian-fetch-adapter.js +0 -53
- package/dist/cjs/testUtils/test-defaults.js +0 -106
- package/dist/cjs/transport/fetch-with-retry.js +0 -146
- package/dist/cjs/transport/http-client.js +0 -56
- package/dist/cjs/transport/http-dispatcher.js +0 -190
- package/dist/cjs/types/api-version.js +0 -8
- package/dist/cjs/types/entities.js +0 -496
- package/dist/cjs/types/enums.js +0 -37
- package/dist/cjs/types/errors.js +0 -12
- package/dist/cjs/types/http.js +0 -4
- package/dist/cjs/types/index.js +0 -22
- package/dist/cjs/types/requests.js +0 -183
- package/dist/cjs/utils/case-conversion.js +0 -54
- package/dist/cjs/utils/index.js +0 -19
- package/dist/cjs/utils/multipart-upload.js +0 -202
- package/dist/cjs/utils/timestamp-conversion.js +0 -58
- package/dist/cjs/utils/url-helpers.js +0 -133
- package/dist/cjs/utils/uuidv7.js +0 -182
|
@@ -1,7 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Helper functions for creating Comms permalinks (`https://comms.todoist.com/...`).
|
|
3
|
+
*
|
|
4
|
+
* Paths come from `@doist/sdk-kmp`, the shared implementation of Comms link
|
|
5
|
+
* routes, so they stay in step with the other Comms clients. The path builders
|
|
6
|
+
* are used rather than the URL builders so that `getFullCommsURL` can keep
|
|
7
|
+
* serving an arbitrary base URL.
|
|
3
8
|
*/
|
|
9
|
+
import { commsChannelPath, commsConversationPath, commsInboxThreadCommentPath, commsInboxThreadPath, commsMessagePath, commsSavedThreadCommentPath, commsSavedThreadPath, commsThreadCommentPath, commsThreadPath, parseCommsLink, } from '@doist/sdk-kmp/comms';
|
|
4
10
|
const COMMS_BASE_URL = 'https://comms.todoist.com';
|
|
11
|
+
/**
|
|
12
|
+
* Builds a path, falling back to a locally built path when the link builder
|
|
13
|
+
* rejects one of the given ids. These helpers run while parsing API responses,
|
|
14
|
+
* so building a path must never fail the parse.
|
|
15
|
+
*
|
|
16
|
+
* Every error is caught, not just id rejection. The link builders expose no
|
|
17
|
+
* error type to narrow on, and a path is never important enough to fail a
|
|
18
|
+
* parse over, so any failure degrades to the locally built path.
|
|
19
|
+
*
|
|
20
|
+
* @param build Builds the path.
|
|
21
|
+
* @param fallback Builds the path without the shared link builder.
|
|
22
|
+
* @returns The built path, or the locally built path if an id was rejected.
|
|
23
|
+
*/
|
|
24
|
+
function buildPath(build, fallback) {
|
|
25
|
+
try {
|
|
26
|
+
return build();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return fallback();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
5
32
|
/**
|
|
6
33
|
* Builds a relative Comms URL based on the provided parameters
|
|
7
34
|
* @param params - URL parameters including workspace, channel, conversation, thread, etc.
|
|
@@ -12,32 +39,33 @@ const COMMS_BASE_URL = 'https://comms.todoist.com';
|
|
|
12
39
|
*/
|
|
13
40
|
export function getCommsURL(params) {
|
|
14
41
|
const { workspaceId, channelId, conversationId, threadId, commentId, messageId, userId } = params;
|
|
15
|
-
|
|
42
|
+
const workspace = String(workspaceId);
|
|
16
43
|
if (channelId) {
|
|
17
|
-
|
|
44
|
+
if (threadId && commentId) {
|
|
45
|
+
return buildPath(() => commsThreadCommentPath(workspace, channelId, threadId, commentId), () => `/${workspace}/ch/${channelId}/t/${threadId}/c/${commentId}`);
|
|
46
|
+
}
|
|
18
47
|
if (threadId) {
|
|
19
|
-
|
|
20
|
-
if (commentId) {
|
|
21
|
-
url += `c/${commentId}`;
|
|
22
|
-
}
|
|
48
|
+
return buildPath(() => commsThreadPath(workspace, channelId, threadId), () => `/${workspace}/ch/${channelId}/t/${threadId}/`);
|
|
23
49
|
}
|
|
50
|
+
return buildPath(() => commsChannelPath(workspace, channelId), () => `/${workspace}/ch/${channelId}/`);
|
|
24
51
|
}
|
|
25
|
-
|
|
26
|
-
url += `inbox/t/${threadId}/`;
|
|
52
|
+
if (threadId) {
|
|
27
53
|
if (commentId) {
|
|
28
|
-
|
|
54
|
+
return buildPath(() => commsInboxThreadCommentPath(workspace, threadId, commentId), () => `/${workspace}/inbox/t/${threadId}/c/${commentId}`);
|
|
29
55
|
}
|
|
56
|
+
return buildPath(() => commsInboxThreadPath(workspace, threadId), () => `/${workspace}/inbox/t/${threadId}/`);
|
|
30
57
|
}
|
|
31
|
-
|
|
32
|
-
url += `msg/${conversationId}/`;
|
|
58
|
+
if (conversationId) {
|
|
33
59
|
if (messageId) {
|
|
34
|
-
|
|
60
|
+
return buildPath(() => commsMessagePath(workspace, conversationId, messageId), () => `/${workspace}/msg/${conversationId}/m/${messageId}`);
|
|
35
61
|
}
|
|
62
|
+
return buildPath(() => commsConversationPath(workspace, conversationId), () => `/${workspace}/msg/${conversationId}/`);
|
|
36
63
|
}
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
// The people view and the bare workspace root have no shared route.
|
|
65
|
+
if (userId) {
|
|
66
|
+
return `/${workspace}/people/u/${userId}`;
|
|
39
67
|
}
|
|
40
|
-
return
|
|
68
|
+
return `/${workspace}/`;
|
|
41
69
|
}
|
|
42
70
|
/**
|
|
43
71
|
* Builds a full Comms URL (with protocol and hostname) based on the provided parameters
|
|
@@ -92,7 +120,13 @@ export function getSavedThreadsRootURL(workspaceId) {
|
|
|
92
120
|
}
|
|
93
121
|
/** Returns the URL for a saved thread. */
|
|
94
122
|
export function getSavedThreadURL(params) {
|
|
95
|
-
|
|
123
|
+
const workspace = String(params.workspaceId);
|
|
124
|
+
return buildPath(() => commsSavedThreadPath(workspace, params.threadId), () => `/${workspace}/saved/t/${params.threadId}`);
|
|
125
|
+
}
|
|
126
|
+
/** Returns the URL for a comment on a saved thread. */
|
|
127
|
+
export function getSavedThreadCommentURL(params) {
|
|
128
|
+
const workspace = String(params.workspaceId);
|
|
129
|
+
return buildPath(() => commsSavedThreadCommentPath(workspace, params.threadId, params.commentId), () => `/${workspace}/saved/t/${params.threadId}/c/${params.commentId}`);
|
|
96
130
|
}
|
|
97
131
|
/** Returns the URL for the search root. */
|
|
98
132
|
export function getSearchRootURL(workspaceId) {
|
|
@@ -112,3 +146,57 @@ export function getSettingsURL(params) {
|
|
|
112
146
|
export function getTeamMembersRootURL(workspaceId) {
|
|
113
147
|
return `/${workspaceId}/people/u`;
|
|
114
148
|
}
|
|
149
|
+
/** The kinds of Comms entity a URL can point at. */
|
|
150
|
+
export const COMMS_LINK_TYPES = [
|
|
151
|
+
'channel',
|
|
152
|
+
'thread',
|
|
153
|
+
'thread_comment',
|
|
154
|
+
'inbox_thread',
|
|
155
|
+
'inbox_thread_comment',
|
|
156
|
+
'saved_thread',
|
|
157
|
+
'saved_thread_comment',
|
|
158
|
+
'conversation',
|
|
159
|
+
'message',
|
|
160
|
+
];
|
|
161
|
+
/**
|
|
162
|
+
* Maps the link parser's type names onto the types this SDK exposes. A name
|
|
163
|
+
* that is missing here is one this SDK does not know about, and is treated as
|
|
164
|
+
* an unrecognised URL rather than surfaced as a value outside the union.
|
|
165
|
+
*/
|
|
166
|
+
const LINK_TYPE_BY_PARSED_NAME = {
|
|
167
|
+
Channel: 'channel',
|
|
168
|
+
Thread: 'thread',
|
|
169
|
+
ThreadComment: 'thread_comment',
|
|
170
|
+
InboxThread: 'inbox_thread',
|
|
171
|
+
InboxThreadComment: 'inbox_thread_comment',
|
|
172
|
+
SavedThread: 'saved_thread',
|
|
173
|
+
SavedThreadComment: 'saved_thread_comment',
|
|
174
|
+
Conversation: 'conversation',
|
|
175
|
+
Message: 'message',
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Parse a Comms URL into the entity it points at.
|
|
179
|
+
*
|
|
180
|
+
* @param url The URL to parse.
|
|
181
|
+
* @returns The entity the URL points at, or `null` if it is not a Comms URL.
|
|
182
|
+
*/
|
|
183
|
+
export function parseCommsURL(url) {
|
|
184
|
+
const link = parseCommsLink(url);
|
|
185
|
+
if (!link) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const type = LINK_TYPE_BY_PARSED_NAME[link.type.name];
|
|
189
|
+
if (!type) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
type,
|
|
194
|
+
workspaceId: link.workspaceId,
|
|
195
|
+
channelId: link.channelId ?? null,
|
|
196
|
+
threadId: link.threadId ?? null,
|
|
197
|
+
commentId: link.commentId ?? null,
|
|
198
|
+
conversationId: link.conversationId ?? null,
|
|
199
|
+
messageId: link.messageId ?? null,
|
|
200
|
+
isQuoteReference: link.isQuoteReference,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Helper functions for creating Comms permalinks (`https://comms.todoist.com/...`).
|
|
3
|
+
*
|
|
4
|
+
* Paths come from `@doist/sdk-kmp`, the shared implementation of Comms link
|
|
5
|
+
* routes, so they stay in step with the other Comms clients. The path builders
|
|
6
|
+
* are used rather than the URL builders so that `getFullCommsURL` can keep
|
|
7
|
+
* serving an arbitrary base URL.
|
|
3
8
|
*/
|
|
4
9
|
export type CommsURLParams = {
|
|
5
10
|
workspaceId: number;
|
|
@@ -72,6 +77,12 @@ export declare function getSavedThreadURL(params: {
|
|
|
72
77
|
workspaceId: number;
|
|
73
78
|
threadId: string;
|
|
74
79
|
}): string;
|
|
80
|
+
/** Returns the URL for a comment on a saved thread. */
|
|
81
|
+
export declare function getSavedThreadCommentURL(params: {
|
|
82
|
+
workspaceId: number;
|
|
83
|
+
threadId: string;
|
|
84
|
+
commentId: string;
|
|
85
|
+
}): string;
|
|
75
86
|
/** Returns the URL for the search root. */
|
|
76
87
|
export declare function getSearchRootURL(workspaceId: number): string;
|
|
77
88
|
/** Returns the URL for a search with a query. */
|
|
@@ -86,3 +97,33 @@ export declare function getSettingsURL(params: {
|
|
|
86
97
|
}): string;
|
|
87
98
|
/** Returns the URL for the team members root. */
|
|
88
99
|
export declare function getTeamMembersRootURL(workspaceId: number): string;
|
|
100
|
+
/** The kinds of Comms entity a URL can point at. */
|
|
101
|
+
export declare const COMMS_LINK_TYPES: readonly ["channel", "thread", "thread_comment", "inbox_thread", "inbox_thread_comment", "saved_thread", "saved_thread_comment", "conversation", "message"];
|
|
102
|
+
/** The kind of Comms entity a URL points at. */
|
|
103
|
+
export type CommsLinkType = (typeof COMMS_LINK_TYPES)[number];
|
|
104
|
+
/** The entity a Comms URL points at. */
|
|
105
|
+
export type CommsURLInfo = {
|
|
106
|
+
/** The kind of entity the URL points at. */
|
|
107
|
+
type: CommsLinkType;
|
|
108
|
+
/** The ID of the workspace the entity belongs to. */
|
|
109
|
+
workspaceId: string;
|
|
110
|
+
/** The ID of the channel, when the URL names one. */
|
|
111
|
+
channelId: string | null;
|
|
112
|
+
/** The ID of the thread, when the URL names one. */
|
|
113
|
+
threadId: string | null;
|
|
114
|
+
/** The ID of the comment, when the URL names one. */
|
|
115
|
+
commentId: string | null;
|
|
116
|
+
/** The ID of the conversation, when the URL names one. */
|
|
117
|
+
conversationId: string | null;
|
|
118
|
+
/** The ID of the message, when the URL names one. */
|
|
119
|
+
messageId: string | null;
|
|
120
|
+
/** Whether the URL points at a quoted reference rather than the entity itself. */
|
|
121
|
+
isQuoteReference: boolean;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Parse a Comms URL into the entity it points at.
|
|
125
|
+
*
|
|
126
|
+
* @param url The URL to parse.
|
|
127
|
+
* @returns The entity the URL points at, or `null` if it is not a Comms URL.
|
|
128
|
+
*/
|
|
129
|
+
export declare function parseCommsURL(url: string): CommsURLInfo | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/comms-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "A TypeScript wrapper for the Comms REST API.",
|
|
5
5
|
"author": "Doist developers",
|
|
6
6
|
"homepage": "https://doist.github.io/comms-sdk-typescript/",
|
|
@@ -15,14 +15,11 @@
|
|
|
15
15
|
"node": ">=24",
|
|
16
16
|
"npm": ">=11"
|
|
17
17
|
},
|
|
18
|
-
"main": "dist/cjs/index.js",
|
|
19
|
-
"module": "dist/esm/index.js",
|
|
20
18
|
"types": "dist/types/index.d.ts",
|
|
21
19
|
"exports": {
|
|
22
20
|
".": {
|
|
23
21
|
"types": "./dist/types/index.d.ts",
|
|
24
|
-
"
|
|
25
|
-
"require": "./dist/cjs/index.js"
|
|
22
|
+
"default": "./dist/esm/index.js"
|
|
26
23
|
}
|
|
27
24
|
},
|
|
28
25
|
"sideEffects": false,
|
|
@@ -31,22 +28,21 @@
|
|
|
31
28
|
"test": "vitest run",
|
|
32
29
|
"test:watch": "vitest",
|
|
33
30
|
"test:coverage": "vitest run --coverage",
|
|
34
|
-
"build:cjs": "npx tsc -p tsconfig.cjs.json",
|
|
35
31
|
"build:esm": "npx tsc -p tsconfig.esm.json",
|
|
36
32
|
"build:fix-esm": "node scripts/fix-esm-imports.cjs",
|
|
37
33
|
"build:fix-dts": "node scripts/fix-dts-imports.cjs",
|
|
38
|
-
"build
|
|
39
|
-
"build": "npm-run-all clean build:cjs build:esm build:fix-esm build:fix-dts build:post",
|
|
34
|
+
"build": "npm-run-all clean build:esm build:fix-esm build:fix-dts",
|
|
40
35
|
"type-check": "npx tsc --noEmit",
|
|
41
36
|
"check": "oxlint src && oxfmt --check",
|
|
42
37
|
"check:fix": "oxlint src --fix && oxfmt",
|
|
43
|
-
"attw": "attw --pack --ignore-rules
|
|
38
|
+
"attw": "attw --pack --ignore-rules cjs-resolves-to-esm",
|
|
44
39
|
"audit": "npm audit --audit-level=moderate",
|
|
45
40
|
"integrity-checks": "npm-run-all clean check test build attw",
|
|
46
41
|
"prepublishOnly": "npm run integrity-checks",
|
|
47
42
|
"prepare": "npm run build"
|
|
48
43
|
},
|
|
49
44
|
"dependencies": {
|
|
45
|
+
"@doist/sdk-kmp": "0.2.3",
|
|
50
46
|
"camelcase": "9.0.0",
|
|
51
47
|
"ts-custom-error": "3.3.1",
|
|
52
48
|
"undici": "7.29.0",
|
|
@@ -81,7 +77,6 @@
|
|
|
81
77
|
"*.{ts,tsx,json,md,yml,yaml}": "oxfmt --no-error-on-unmatched-pattern"
|
|
82
78
|
},
|
|
83
79
|
"files": [
|
|
84
|
-
"dist/cjs/**/*",
|
|
85
80
|
"dist/esm/**/*",
|
|
86
81
|
"dist/types/**/*",
|
|
87
82
|
"!dist/**/*.test.js",
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TOKEN_ENDPOINT_AUTH_METHODS = exports.COMMS_SCOPES = void 0;
|
|
4
|
-
exports.getAuthStateParameter = getAuthStateParameter;
|
|
5
|
-
exports.getAuthorizationUrl = getAuthorizationUrl;
|
|
6
|
-
exports.getAuthToken = getAuthToken;
|
|
7
|
-
exports.refreshAuthToken = refreshAuthToken;
|
|
8
|
-
exports.revokeAuthToken = revokeAuthToken;
|
|
9
|
-
exports.registerClient = registerClient;
|
|
10
|
-
const uuid_1 = require("uuid");
|
|
11
|
-
const http_client_1 = require("./transport/http-client");
|
|
12
|
-
const errors_1 = require("./types/errors");
|
|
13
|
-
/**
|
|
14
|
-
* Default origin of the Comms authorization server.
|
|
15
|
-
*
|
|
16
|
-
* The authorization server for Comms is Todoist (`todoist.com`), not the
|
|
17
|
-
* resource server (`comms.todoist.com`). Tokens are issued by Todoist and
|
|
18
|
-
* accepted by the Comms API. Paths match Todoist's published
|
|
19
|
-
* `/.well-known/oauth-authorization-server` metadata.
|
|
20
|
-
*/
|
|
21
|
-
const DEFAULT_AUTH_BASE_URL = 'https://todoist.com';
|
|
22
|
-
function getAuthBaseUrl(baseUrl) {
|
|
23
|
-
const base = baseUrl ?? DEFAULT_AUTH_BASE_URL;
|
|
24
|
-
return base.endsWith('/') ? base.slice(0, -1) : base;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* OAuth scopes for the Comms API.
|
|
28
|
-
*
|
|
29
|
-
* @remarks
|
|
30
|
-
* Request only the scopes your application needs:
|
|
31
|
-
*
|
|
32
|
-
* **User Scopes:**
|
|
33
|
-
* - `user:read` - Access user's personal settings
|
|
34
|
-
* - `user:write` - Access and update user's personal settings
|
|
35
|
-
*
|
|
36
|
-
* **Workspace Scopes:**
|
|
37
|
-
* - `workspaces:read` - Access teams the user is part of
|
|
38
|
-
* - `workspaces:write` - Access and update teams the user is part of
|
|
39
|
-
*
|
|
40
|
-
* **Channel Scopes:**
|
|
41
|
-
* - `channels:read` - Access channels
|
|
42
|
-
* - `channels:write` - Access and update channels
|
|
43
|
-
* - `channels:remove` - Access, update, and delete channels
|
|
44
|
-
*
|
|
45
|
-
* **Thread Scopes:**
|
|
46
|
-
* - `threads:read` - Access threads
|
|
47
|
-
* - `threads:write` - Access and update threads
|
|
48
|
-
* - `threads:remove` - Access, update, and delete threads
|
|
49
|
-
*
|
|
50
|
-
* **Comment Scopes:**
|
|
51
|
-
* - `comments:read` - Access comments
|
|
52
|
-
* - `comments:write` - Access and update comments
|
|
53
|
-
* - `comments:remove` - Access, update, and delete comments
|
|
54
|
-
*
|
|
55
|
-
* **Group Scopes:**
|
|
56
|
-
* - `groups:read` - Access groups
|
|
57
|
-
* - `groups:write` - Access and update groups
|
|
58
|
-
* - `groups:remove` - Access, update, and delete groups
|
|
59
|
-
*
|
|
60
|
-
* **Message Scopes:**
|
|
61
|
-
* - `messages:read` - Access messages
|
|
62
|
-
* - `messages:write` - Access and update messages
|
|
63
|
-
* - `messages:remove` - Access, update, and delete messages
|
|
64
|
-
*
|
|
65
|
-
* **Reaction Scopes:**
|
|
66
|
-
* - `reactions:read` - Access reactions
|
|
67
|
-
* - `reactions:write` - Access and update reactions
|
|
68
|
-
* - `reactions:remove` - Access, update, and delete reactions
|
|
69
|
-
*
|
|
70
|
-
* **Search Scopes:**
|
|
71
|
-
* - `search:read` - Search
|
|
72
|
-
*
|
|
73
|
-
* **Attachment Scopes:**
|
|
74
|
-
* - `attachments:read` - Access attachments
|
|
75
|
-
* - `attachments:write` - Access and update attachments
|
|
76
|
-
*
|
|
77
|
-
* **Notification Scopes:**
|
|
78
|
-
* - `notifications:read` - Read user's notifications settings
|
|
79
|
-
* - `notifications:write` - Read and update user's notifications settings
|
|
80
|
-
*/
|
|
81
|
-
exports.COMMS_SCOPES = [
|
|
82
|
-
'user:read',
|
|
83
|
-
'user:write',
|
|
84
|
-
'workspaces:read',
|
|
85
|
-
'workspaces:write',
|
|
86
|
-
'channels:read',
|
|
87
|
-
'channels:write',
|
|
88
|
-
'channels:remove',
|
|
89
|
-
'threads:read',
|
|
90
|
-
'threads:write',
|
|
91
|
-
'threads:remove',
|
|
92
|
-
'comments:read',
|
|
93
|
-
'comments:write',
|
|
94
|
-
'comments:remove',
|
|
95
|
-
'groups:read',
|
|
96
|
-
'groups:write',
|
|
97
|
-
'groups:remove',
|
|
98
|
-
'messages:read',
|
|
99
|
-
'messages:write',
|
|
100
|
-
'messages:remove',
|
|
101
|
-
'reactions:read',
|
|
102
|
-
'reactions:write',
|
|
103
|
-
'reactions:remove',
|
|
104
|
-
'search:read',
|
|
105
|
-
'attachments:read',
|
|
106
|
-
'attachments:write',
|
|
107
|
-
'notifications:read',
|
|
108
|
-
'notifications:write',
|
|
109
|
-
];
|
|
110
|
-
/** Supported token endpoint authentication methods for dynamic client registration. */
|
|
111
|
-
exports.TOKEN_ENDPOINT_AUTH_METHODS = [
|
|
112
|
-
'client_secret_post',
|
|
113
|
-
'client_secret_basic',
|
|
114
|
-
'none',
|
|
115
|
-
];
|
|
116
|
-
function getAuthStateParameter() {
|
|
117
|
-
return (0, uuid_1.v4)();
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Generates the authorization URL for the OAuth2 flow.
|
|
121
|
-
*
|
|
122
|
-
* The `clientId` can be either a traditional client ID string (e.g. from
|
|
123
|
-
* {@link registerClient}) or an HTTPS URL pointing to a client metadata document,
|
|
124
|
-
* as defined in {@link https://drafts.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/ RFC draft-ietf-oauth-client-id-metadata-document}.
|
|
125
|
-
*/
|
|
126
|
-
function getAuthorizationUrl(clientId, scopes, state, redirectUri, baseUrl) {
|
|
127
|
-
if (!scopes?.length) {
|
|
128
|
-
throw new Error('At least one scope value is required.');
|
|
129
|
-
}
|
|
130
|
-
const authBaseUrl = `${getAuthBaseUrl(baseUrl)}/oauth`;
|
|
131
|
-
const scope = scopes.join(' ');
|
|
132
|
-
const params = new URLSearchParams({
|
|
133
|
-
client_id: clientId,
|
|
134
|
-
response_type: 'code',
|
|
135
|
-
scope,
|
|
136
|
-
state,
|
|
137
|
-
});
|
|
138
|
-
if (redirectUri) {
|
|
139
|
-
params.append('redirect_uri', redirectUri);
|
|
140
|
-
}
|
|
141
|
-
return `${authBaseUrl}/authorize?${params.toString()}`;
|
|
142
|
-
}
|
|
143
|
-
async function getAuthToken(args, options) {
|
|
144
|
-
const tokenUrl = `${getAuthBaseUrl(options?.baseUrl)}/oauth/access_token`;
|
|
145
|
-
const payload = {
|
|
146
|
-
clientId: args.clientId,
|
|
147
|
-
clientSecret: args.clientSecret,
|
|
148
|
-
code: args.code,
|
|
149
|
-
grantType: 'authorization_code',
|
|
150
|
-
...(args.redirectUri && { redirectUri: args.redirectUri }),
|
|
151
|
-
};
|
|
152
|
-
const response = await (0, http_client_1.request)({
|
|
153
|
-
httpMethod: 'POST',
|
|
154
|
-
baseUri: tokenUrl,
|
|
155
|
-
relativePath: '',
|
|
156
|
-
payload,
|
|
157
|
-
customFetch: options?.customFetch,
|
|
158
|
-
});
|
|
159
|
-
if (!(0, http_client_1.isSuccess)(response) || !response.data?.accessToken) {
|
|
160
|
-
throw new errors_1.CommsRequestError('Authentication token exchange failed.', response.status, response.data);
|
|
161
|
-
}
|
|
162
|
-
return response.data;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Exchanges a refresh token for a new access token using the OAuth2
|
|
166
|
-
* `refresh_token` grant.
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```typescript
|
|
170
|
-
* const { accessToken, refreshToken } = await refreshAuthToken({
|
|
171
|
-
* clientId: 'client-id',
|
|
172
|
-
* clientSecret: 'client-secret',
|
|
173
|
-
* refreshToken: storedRefreshToken,
|
|
174
|
-
* })
|
|
175
|
-
* ```
|
|
176
|
-
*
|
|
177
|
-
* @returns The refreshed token. The response may include a new `refreshToken`
|
|
178
|
-
* (rotated by the server); persist it in place of the previous one when present.
|
|
179
|
-
* @throws {@link CommsRequestError} If the refresh fails
|
|
180
|
-
*/
|
|
181
|
-
async function refreshAuthToken(args, options) {
|
|
182
|
-
const tokenUrl = `${getAuthBaseUrl(options?.baseUrl)}/oauth/access_token`;
|
|
183
|
-
const payload = {
|
|
184
|
-
clientId: args.clientId,
|
|
185
|
-
clientSecret: args.clientSecret,
|
|
186
|
-
refreshToken: args.refreshToken,
|
|
187
|
-
grantType: 'refresh_token',
|
|
188
|
-
};
|
|
189
|
-
const response = await (0, http_client_1.request)({
|
|
190
|
-
httpMethod: 'POST',
|
|
191
|
-
baseUri: tokenUrl,
|
|
192
|
-
relativePath: '',
|
|
193
|
-
payload,
|
|
194
|
-
customFetch: options?.customFetch,
|
|
195
|
-
});
|
|
196
|
-
if (!(0, http_client_1.isSuccess)(response) || !response.data?.accessToken) {
|
|
197
|
-
throw new errors_1.CommsRequestError('Authentication token refresh failed.', response.status, response.data);
|
|
198
|
-
}
|
|
199
|
-
return response.data;
|
|
200
|
-
}
|
|
201
|
-
async function revokeAuthToken(args, options) {
|
|
202
|
-
const revokeUrl = `${getAuthBaseUrl(options?.baseUrl)}/api/v1/revoke`;
|
|
203
|
-
const response = await (0, http_client_1.request)({
|
|
204
|
-
httpMethod: 'POST',
|
|
205
|
-
baseUri: revokeUrl,
|
|
206
|
-
relativePath: '',
|
|
207
|
-
payload: {
|
|
208
|
-
clientId: args.clientId,
|
|
209
|
-
clientSecret: args.clientSecret,
|
|
210
|
-
token: args.accessToken,
|
|
211
|
-
},
|
|
212
|
-
customFetch: options?.customFetch,
|
|
213
|
-
});
|
|
214
|
-
return (0, http_client_1.isSuccess)(response);
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Registers a new OAuth client via Dynamic Client Registration (RFC 7591).
|
|
218
|
-
*
|
|
219
|
-
* @example
|
|
220
|
-
* ```typescript
|
|
221
|
-
* const client = await registerClient({
|
|
222
|
-
* redirectUris: ['https://example.com/callback'],
|
|
223
|
-
* clientName: 'My App',
|
|
224
|
-
* scope: ['user:read', 'channels:read'],
|
|
225
|
-
* })
|
|
226
|
-
* // Use client.clientId and client.clientSecret for OAuth flows
|
|
227
|
-
* ```
|
|
228
|
-
*
|
|
229
|
-
* @returns The registered client details
|
|
230
|
-
* @throws {@link CommsRequestError} If the registration fails
|
|
231
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc7591 RFC 7591}
|
|
232
|
-
*/
|
|
233
|
-
async function registerClient(args, options) {
|
|
234
|
-
const registerUrl = `${getAuthBaseUrl(options?.baseUrl)}/oauth/register`;
|
|
235
|
-
const response = await (0, http_client_1.request)({
|
|
236
|
-
httpMethod: 'POST',
|
|
237
|
-
baseUri: registerUrl,
|
|
238
|
-
relativePath: '',
|
|
239
|
-
payload: { ...args, scope: args.scope?.join(' ') },
|
|
240
|
-
customFetch: options?.customFetch,
|
|
241
|
-
});
|
|
242
|
-
if (!(0, http_client_1.isSuccess)(response) || !response.data?.clientId) {
|
|
243
|
-
throw new errors_1.CommsRequestError('Dynamic client registration failed.', response.status, response.data);
|
|
244
|
-
}
|
|
245
|
-
const { clientIdIssuedAt, clientSecretExpiresAt, scope, ...rest } = response.data;
|
|
246
|
-
return {
|
|
247
|
-
...rest,
|
|
248
|
-
scope: scope ? scope.split(' ') : undefined,
|
|
249
|
-
clientIdIssuedAt: clientIdIssuedAt !== undefined ? new Date(clientIdIssuedAt * 1000) : undefined,
|
|
250
|
-
clientSecretExpiresAt: clientSecretExpiresAt === undefined
|
|
251
|
-
? undefined
|
|
252
|
-
: clientSecretExpiresAt === 0
|
|
253
|
-
? null
|
|
254
|
-
: new Date(clientSecretExpiresAt * 1000),
|
|
255
|
-
};
|
|
256
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addCommentRequest = addCommentRequest;
|
|
4
|
-
const endpoints_1 = require("../consts/endpoints");
|
|
5
|
-
const http_client_1 = require("../transport/http-client");
|
|
6
|
-
const uuidv7_1 = require("../utils/uuidv7");
|
|
7
|
-
const notify_audience_1 = require("./notify-audience");
|
|
8
|
-
/**
|
|
9
|
-
* Internal helper that powers `comments.createComment`,
|
|
10
|
-
* `threads.closeThread`, and `threads.reopenThread`.
|
|
11
|
-
*
|
|
12
|
-
* Normalizes the `notifyAudience` flag into a sentinel `groups` entry,
|
|
13
|
-
* rejects sentinel IDs passed via `groups` / `directGroupMentions`, mints a
|
|
14
|
-
* UUIDv7 `id` when the caller omits one, and posts to `/comments/add`. When
|
|
15
|
-
* `threadAction` is set (`'close'` / `'reopen'`), it is forwarded on the
|
|
16
|
-
* wire so the same request both adds the comment and transitions the
|
|
17
|
-
* parent thread.
|
|
18
|
-
*
|
|
19
|
-
* @param context - Per-call client context (base URI, API token, optional `customFetch`).
|
|
20
|
-
* @param params - The comment payload (`{@link CreateCommentArgs}`).
|
|
21
|
-
* @param options - Optional configuration.
|
|
22
|
-
* @param options.threadAction - When set, also transitions the parent thread (`'close'` or `'reopen'`).
|
|
23
|
-
* @returns The parsed {@link Comment} returned by the API.
|
|
24
|
-
*/
|
|
25
|
-
function addCommentRequest(context, params, options) {
|
|
26
|
-
const normalized = (0, notify_audience_1.applyNotifyAudience)(params);
|
|
27
|
-
const withId = {
|
|
28
|
-
...normalized,
|
|
29
|
-
id: (0, uuidv7_1.resolveCreateId)(normalized.id),
|
|
30
|
-
threadId: (0, uuidv7_1.resolveReferenceId)(normalized.threadId, 'threadId'),
|
|
31
|
-
};
|
|
32
|
-
const payload = options?.threadAction
|
|
33
|
-
? { ...withId, threadAction: options.threadAction }
|
|
34
|
-
: withId;
|
|
35
|
-
return (0, http_client_1.request)({
|
|
36
|
-
httpMethod: 'POST',
|
|
37
|
-
baseUri: context.baseUri,
|
|
38
|
-
relativePath: `${endpoints_1.ENDPOINT_COMMENTS}/add`,
|
|
39
|
-
apiToken: context.apiToken,
|
|
40
|
-
payload,
|
|
41
|
-
customFetch: context.customFetch,
|
|
42
|
-
}).then((response) => context.schema.parse(response.data));
|
|
43
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AttachmentsClient = void 0;
|
|
4
|
-
const endpoints_1 = require("../consts/endpoints");
|
|
5
|
-
const entities_1 = require("../types/entities");
|
|
6
|
-
const multipart_upload_1 = require("../utils/multipart-upload");
|
|
7
|
-
const uuidv7_1 = require("../utils/uuidv7");
|
|
8
|
-
const base_client_1 = require("./base-client");
|
|
9
|
-
/**
|
|
10
|
-
* Resolve the `attachment_id`: validate a caller-supplied value (throwing an
|
|
11
|
-
* {@link UuidV7Error} that names `attachmentId` rather than the generic `id`) or
|
|
12
|
-
* mint a fresh one. Wraps {@link resolveCreateId} so the error contract matches the
|
|
13
|
-
* client's actual parameter.
|
|
14
|
-
*/
|
|
15
|
-
function resolveAttachmentId(attachmentId) {
|
|
16
|
-
if (attachmentId !== undefined && !(0, uuidv7_1.isValidUuidV7Base58)(attachmentId)) {
|
|
17
|
-
throw new uuidv7_1.UuidV7Error(`invalid attachmentId ${JSON.stringify(attachmentId)} — use generateId() or omit \`attachmentId\` and let the SDK mint one.`);
|
|
18
|
-
}
|
|
19
|
-
return (0, uuidv7_1.resolveCreateId)(attachmentId);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Client for uploading file attachments to Comms.
|
|
23
|
-
*
|
|
24
|
-
* Attachments are uploaded independently, then referenced by passing the returned
|
|
25
|
-
* {@link Attachment} into the `attachments` array of `comments.createComment`,
|
|
26
|
-
* `conversationMessages.createMessage`, and similar calls.
|
|
27
|
-
*/
|
|
28
|
-
class AttachmentsClient extends base_client_1.BaseClient {
|
|
29
|
-
/**
|
|
30
|
-
* Uploads a file and returns the created {@link Attachment}.
|
|
31
|
-
*
|
|
32
|
-
* Mirrors the canonical multipart upload: `POST /attachments/upload` with the `file`
|
|
33
|
-
* binary plus `file_name`, `file_size`, `attachment_id`, and `underlying_type` form
|
|
34
|
-
* fields.
|
|
35
|
-
*
|
|
36
|
-
* @param args - The file to upload and optional metadata.
|
|
37
|
-
* @returns The created attachment, ready to attach to a comment or message.
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```typescript
|
|
41
|
-
* import { readFile } from 'node:fs/promises'
|
|
42
|
-
*
|
|
43
|
-
* const attachment = await api.attachments.upload({
|
|
44
|
-
* file: await readFile('./diagram.png'),
|
|
45
|
-
* fileName: 'diagram.png',
|
|
46
|
-
* })
|
|
47
|
-
*
|
|
48
|
-
* await api.comments.createComment({
|
|
49
|
-
* threadId: '7YpL3oZ4kZ9vP7Q1tR2sX3z',
|
|
50
|
-
* content: 'See attached',
|
|
51
|
-
* attachments: [attachment],
|
|
52
|
-
* })
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
async upload(args) {
|
|
56
|
-
const data = await (0, multipart_upload_1.uploadMultipartFile)({
|
|
57
|
-
baseUrl: this.getBaseUri(),
|
|
58
|
-
authToken: this.apiToken,
|
|
59
|
-
endpoint: `${endpoints_1.ENDPOINT_ATTACHMENTS}/upload`,
|
|
60
|
-
file: args.file,
|
|
61
|
-
fileName: args.fileName,
|
|
62
|
-
contentType: args.contentType,
|
|
63
|
-
additionalFields: {
|
|
64
|
-
attachment_id: resolveAttachmentId(args.attachmentId),
|
|
65
|
-
},
|
|
66
|
-
customFetch: this.customFetch,
|
|
67
|
-
});
|
|
68
|
-
return entities_1.AttachmentSchema.parse(data);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
exports.AttachmentsClient = AttachmentsClient;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BaseClient = void 0;
|
|
4
|
-
const endpoints_1 = require("../consts/endpoints");
|
|
5
|
-
const api_version_1 = require("../types/api-version");
|
|
6
|
-
/**
|
|
7
|
-
* Base class for every Comms API client. Centralizes URL handling and
|
|
8
|
-
* config so individual clients stay focused on their endpoints.
|
|
9
|
-
*/
|
|
10
|
-
class BaseClient {
|
|
11
|
-
constructor(config) {
|
|
12
|
-
this.apiToken = config.apiToken;
|
|
13
|
-
this.baseUrl = config.baseUrl;
|
|
14
|
-
this.defaultVersion = config.version || api_version_1.DEFAULT_API_VERSION;
|
|
15
|
-
this.customFetch = config.customFetch;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Returns the base URI for an API request, with a guaranteed trailing
|
|
19
|
-
* slash so relative paths resolve cleanly through `URL`.
|
|
20
|
-
*/
|
|
21
|
-
getBaseUri() {
|
|
22
|
-
return (0, endpoints_1.getCommsBaseUri)(this.defaultVersion, this.baseUrl);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Base URL for entity web links, or `undefined` to use getFullCommsURL's
|
|
26
|
-
* default web app. Trailing-slash normalization happens in
|
|
27
|
-
* `getFullCommsURL`, so the configured value is returned verbatim.
|
|
28
|
-
*/
|
|
29
|
-
getLinkBaseUrl() {
|
|
30
|
-
return this.baseUrl;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
exports.BaseClient = BaseClient;
|