@doist/comms-sdk 0.1.0-alpha.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.
Files changed (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/dist/cjs/authentication.js +211 -0
  4. package/dist/cjs/clients/add-comment-helper.js +53 -0
  5. package/dist/cjs/clients/base-client.js +27 -0
  6. package/dist/cjs/clients/channels-client.js +83 -0
  7. package/dist/cjs/clients/comments-client.js +93 -0
  8. package/dist/cjs/clients/conversation-messages-client.js +87 -0
  9. package/dist/cjs/clients/conversations-client.js +103 -0
  10. package/dist/cjs/clients/groups-client.js +71 -0
  11. package/dist/cjs/clients/inbox-client.js +98 -0
  12. package/dist/cjs/clients/reactions-client.js +59 -0
  13. package/dist/cjs/clients/search-client.js +88 -0
  14. package/dist/cjs/clients/threads-client.js +135 -0
  15. package/dist/cjs/clients/users-client.js +199 -0
  16. package/dist/cjs/clients/workspace-users-client.js +140 -0
  17. package/dist/cjs/clients/workspaces-client.js +93 -0
  18. package/dist/cjs/comms-api.js +65 -0
  19. package/dist/cjs/consts/endpoints.js +27 -0
  20. package/dist/cjs/index.js +48 -0
  21. package/dist/cjs/package.json +1 -0
  22. package/dist/cjs/testUtils/msw-handlers.js +51 -0
  23. package/dist/cjs/testUtils/msw-setup.js +21 -0
  24. package/dist/cjs/testUtils/obsidian-fetch-adapter.js +53 -0
  25. package/dist/cjs/testUtils/test-defaults.js +102 -0
  26. package/dist/cjs/transport/fetch-with-retry.js +136 -0
  27. package/dist/cjs/transport/http-client.js +56 -0
  28. package/dist/cjs/transport/http-dispatcher.js +143 -0
  29. package/dist/cjs/types/entities.js +411 -0
  30. package/dist/cjs/types/enums.js +37 -0
  31. package/dist/cjs/types/errors.js +12 -0
  32. package/dist/cjs/types/http.js +4 -0
  33. package/dist/cjs/types/index.js +21 -0
  34. package/dist/cjs/types/requests.js +117 -0
  35. package/dist/cjs/utils/case-conversion.js +54 -0
  36. package/dist/cjs/utils/index.js +19 -0
  37. package/dist/cjs/utils/timestamp-conversion.js +49 -0
  38. package/dist/cjs/utils/url-helpers.js +131 -0
  39. package/dist/cjs/utils/uuidv7.js +174 -0
  40. package/dist/esm/authentication.js +203 -0
  41. package/dist/esm/clients/add-comment-helper.js +50 -0
  42. package/dist/esm/clients/base-client.js +23 -0
  43. package/dist/esm/clients/channels-client.js +79 -0
  44. package/dist/esm/clients/comments-client.js +89 -0
  45. package/dist/esm/clients/conversation-messages-client.js +83 -0
  46. package/dist/esm/clients/conversations-client.js +99 -0
  47. package/dist/esm/clients/groups-client.js +67 -0
  48. package/dist/esm/clients/inbox-client.js +94 -0
  49. package/dist/esm/clients/reactions-client.js +55 -0
  50. package/dist/esm/clients/search-client.js +84 -0
  51. package/dist/esm/clients/threads-client.js +131 -0
  52. package/dist/esm/clients/users-client.js +195 -0
  53. package/dist/esm/clients/workspace-users-client.js +136 -0
  54. package/dist/esm/clients/workspaces-client.js +89 -0
  55. package/dist/esm/comms-api.js +61 -0
  56. package/dist/esm/consts/endpoints.js +23 -0
  57. package/dist/esm/index.js +17 -0
  58. package/dist/esm/testUtils/msw-handlers.js +45 -0
  59. package/dist/esm/testUtils/msw-setup.js +18 -0
  60. package/dist/esm/testUtils/obsidian-fetch-adapter.js +50 -0
  61. package/dist/esm/testUtils/test-defaults.js +99 -0
  62. package/dist/esm/transport/fetch-with-retry.js +133 -0
  63. package/dist/esm/transport/http-client.js +51 -0
  64. package/dist/esm/transport/http-dispatcher.js +104 -0
  65. package/dist/esm/types/entities.js +408 -0
  66. package/dist/esm/types/enums.js +34 -0
  67. package/dist/esm/types/errors.js +8 -0
  68. package/dist/esm/types/http.js +1 -0
  69. package/dist/esm/types/index.js +5 -0
  70. package/dist/esm/types/requests.js +114 -0
  71. package/dist/esm/utils/case-conversion.js +47 -0
  72. package/dist/esm/utils/index.js +3 -0
  73. package/dist/esm/utils/timestamp-conversion.js +45 -0
  74. package/dist/esm/utils/url-helpers.js +112 -0
  75. package/dist/esm/utils/uuidv7.js +163 -0
  76. package/dist/types/authentication.d.ts +160 -0
  77. package/dist/types/clients/add-comment-helper.d.ts +12 -0
  78. package/dist/types/clients/base-client.d.ts +24 -0
  79. package/dist/types/clients/channels-client.d.ts +91 -0
  80. package/dist/types/clients/comments-client.d.ts +157 -0
  81. package/dist/types/clients/conversation-messages-client.d.ts +127 -0
  82. package/dist/types/clients/conversations-client.d.ts +206 -0
  83. package/dist/types/clients/groups-client.d.ts +55 -0
  84. package/dist/types/clients/inbox-client.d.ts +20 -0
  85. package/dist/types/clients/reactions-client.d.ts +19 -0
  86. package/dist/types/clients/search-client.d.ts +20 -0
  87. package/dist/types/clients/threads-client.d.ts +344 -0
  88. package/dist/types/clients/users-client.d.ts +123 -0
  89. package/dist/types/clients/workspace-users-client.d.ts +47 -0
  90. package/dist/types/clients/workspaces-client.d.ts +79 -0
  91. package/dist/types/comms-api.d.ts +59 -0
  92. package/dist/types/consts/endpoints.d.ts +19 -0
  93. package/dist/types/index.d.ts +18 -0
  94. package/dist/types/testUtils/msw-handlers.d.ts +28 -0
  95. package/dist/types/testUtils/msw-setup.d.ts +1 -0
  96. package/dist/types/testUtils/obsidian-fetch-adapter.d.ts +29 -0
  97. package/dist/types/testUtils/test-defaults.d.ts +16 -0
  98. package/dist/types/transport/fetch-with-retry.d.ts +4 -0
  99. package/dist/types/transport/http-client.d.ts +13 -0
  100. package/dist/types/transport/http-dispatcher.d.ts +10 -0
  101. package/dist/types/types/entities.d.ts +1288 -0
  102. package/dist/types/types/enums.d.ts +55 -0
  103. package/dist/types/types/errors.d.ts +6 -0
  104. package/dist/types/types/http.d.ts +54 -0
  105. package/dist/types/types/index.d.ts +5 -0
  106. package/dist/types/types/requests.d.ts +385 -0
  107. package/dist/types/utils/case-conversion.d.ts +8 -0
  108. package/dist/types/utils/index.d.ts +3 -0
  109. package/dist/types/utils/timestamp-conversion.d.ts +13 -0
  110. package/dist/types/utils/url-helpers.d.ts +88 -0
  111. package/dist/types/utils/uuidv7.d.ts +40 -0
  112. package/package.json +93 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Helper functions for creating Comms permalinks (`https://comms.todoist.com/a/...`).
3
+ */
4
+ const COMMS_BASE_URL = 'https://comms.todoist.com';
5
+ /**
6
+ * Builds a relative Comms URL based on the provided parameters
7
+ * @param params - URL parameters including workspace, channel, conversation, thread, etc.
8
+ * @returns A relative URL path
9
+ * @example
10
+ * getCommsURL({ workspaceId: 1, channelId: '7Yp...', threadId: '7Yq...' })
11
+ * // returns '/a/1/ch/7Yp.../t/7Yq.../'
12
+ */
13
+ export function getCommsURL(params) {
14
+ const { workspaceId, channelId, conversationId, threadId, commentId, messageId, userId } = params;
15
+ let url = `/a/${workspaceId}/`;
16
+ if (channelId) {
17
+ url += `ch/${channelId}/`;
18
+ if (threadId) {
19
+ url += `t/${threadId}/`;
20
+ if (commentId) {
21
+ url += `c/${commentId}`;
22
+ }
23
+ }
24
+ }
25
+ else if (threadId) {
26
+ url += `inbox/t/${threadId}/`;
27
+ if (commentId) {
28
+ url += `c/${commentId}`;
29
+ }
30
+ }
31
+ else if (conversationId) {
32
+ url += `msg/${conversationId}/`;
33
+ if (messageId) {
34
+ url += `m/${messageId}`;
35
+ }
36
+ }
37
+ else if (userId) {
38
+ url += `people/u/${userId}`;
39
+ }
40
+ return url;
41
+ }
42
+ /**
43
+ * Builds a full Comms URL (with protocol and hostname) based on the provided parameters
44
+ * @param params - URL parameters including workspace, channel, conversation, thread, etc.
45
+ * @param baseUrl - Optional base URL (defaults to 'https://comms.todoist.com')
46
+ */
47
+ export function getFullCommsURL(params, baseUrl = COMMS_BASE_URL) {
48
+ return `${baseUrl}${getCommsURL(params)}`;
49
+ }
50
+ /** Returns the URL for a thread in a channel. */
51
+ export function getThreadURL(params) {
52
+ return getCommsURL(params);
53
+ }
54
+ /** Returns the URL for a channel. */
55
+ export function getChannelURL(params) {
56
+ return getCommsURL(params);
57
+ }
58
+ /** Returns the URL for a conversation. */
59
+ export function getConversationURL(params) {
60
+ return getCommsURL(params);
61
+ }
62
+ /** Returns the URL for a specific message in a conversation. */
63
+ export function getMessageURL(params) {
64
+ return getCommsURL(params);
65
+ }
66
+ /** Returns the URL for a comment in a thread. */
67
+ export function getCommentURL(params) {
68
+ return getCommsURL(params);
69
+ }
70
+ /** Returns the URL for the threads root (channels view). */
71
+ export function getThreadsRootURL(workspaceId) {
72
+ return `/a/${workspaceId}/ch`;
73
+ }
74
+ /** Returns the URL for the inbox. */
75
+ export function getInboxURL(workspaceId, tab) {
76
+ const tabParam = tab ? `/${tab}` : '';
77
+ return `/a/${workspaceId}/inbox${tabParam}`;
78
+ }
79
+ /** Returns the URL for the messages/conversations root. */
80
+ export function getMessagesRootURL(workspaceId) {
81
+ return `/a/${workspaceId}/msg`;
82
+ }
83
+ /** Returns the URL for a user profile. */
84
+ export function getUserProfileURL(params) {
85
+ return `/a/${params.workspaceId}/people/u/${params.userId}`;
86
+ }
87
+ /** Returns the URL for the saved threads view. */
88
+ export function getSavedThreadsRootURL(workspaceId) {
89
+ return `/a/${workspaceId}/saved`;
90
+ }
91
+ /** Returns the URL for a saved thread. */
92
+ export function getSavedThreadURL(params) {
93
+ return `/a/${params.workspaceId}/saved/t/${params.threadId}`;
94
+ }
95
+ /** Returns the URL for the search root. */
96
+ export function getSearchRootURL(workspaceId) {
97
+ return `/a/${workspaceId}/search`;
98
+ }
99
+ /** Returns the URL for a search with a query. */
100
+ export function getSearchQueryURL(params) {
101
+ return `/a/${params.workspaceId}/search?q=${decodeURIComponent(params.query)}`;
102
+ }
103
+ /** Returns the URL for settings. */
104
+ export function getSettingsURL(params) {
105
+ return params.initialLocation
106
+ ? `/a/${params.workspaceId}/settings/${params.initialLocation}`
107
+ : `/a/${params.workspaceId}/settings`;
108
+ }
109
+ /** Returns the URL for the team members root. */
110
+ export function getTeamMembersRootURL(workspaceId) {
111
+ return `/a/${workspaceId}/people/u`;
112
+ }
@@ -0,0 +1,163 @@
1
+ import { v7 as uuidv7 } from 'uuid';
2
+ /**
3
+ * ID utilities for entities that use opaque string identifiers (channels,
4
+ * threads, comments, conversations, messages, groups).
5
+ *
6
+ * Use {@link generateId} to mint a new ID locally and pass it to a creation
7
+ * endpoint. {@link encodeUuidToBase58} / {@link decodeBase58ToUuidBytes}
8
+ * expose the underlying encoding for callers that need to round-trip raw
9
+ * UUID bytes themselves.
10
+ */
11
+ const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
12
+ const BASE58_MAP = (() => {
13
+ const map = {};
14
+ for (let i = 0; i < BASE58_ALPHABET.length; i++) {
15
+ map[BASE58_ALPHABET[i]] = i;
16
+ }
17
+ return map;
18
+ })();
19
+ const UUID_BYTES_LEN = 16;
20
+ const UUID_BASE58_MAX_LEN = 22;
21
+ const HEX_RE = /^[0-9a-f]+$/;
22
+ export class UuidV7Error extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = 'UuidV7Error';
26
+ }
27
+ }
28
+ /** Encode a 16-byte UUID as a base58 string. */
29
+ export function encodeUuidToBase58(bytes) {
30
+ if (bytes.length !== UUID_BYTES_LEN) {
31
+ throw new UuidV7Error(`id must be ${UUID_BYTES_LEN} bytes`);
32
+ }
33
+ let zeros = 0;
34
+ while (zeros < bytes.length && bytes[zeros] === 0) {
35
+ zeros++;
36
+ }
37
+ let n = 0n;
38
+ for (const b of bytes) {
39
+ n = (n << 8n) | BigInt(b);
40
+ }
41
+ let out = '';
42
+ while (n > 0n) {
43
+ const rem = Number(n % 58n);
44
+ out = BASE58_ALPHABET[rem] + out;
45
+ n /= 58n;
46
+ }
47
+ return '1'.repeat(zeros) + out;
48
+ }
49
+ /**
50
+ * Decode a base58 string back into 16 UUID bytes. Throws {@link UuidV7Error}
51
+ * if the input is malformed or doesn't decode to exactly 16 bytes.
52
+ */
53
+ export function decodeBase58ToUuidBytes(value) {
54
+ if (typeof value !== 'string') {
55
+ throw new UuidV7Error('id must be a base58 string');
56
+ }
57
+ if (value.length === 0) {
58
+ throw new UuidV7Error('id is empty');
59
+ }
60
+ if (value.length > UUID_BASE58_MAX_LEN) {
61
+ throw new UuidV7Error('id is too long');
62
+ }
63
+ let zeros = 0;
64
+ while (zeros < value.length && value[zeros] === '1') {
65
+ zeros++;
66
+ }
67
+ let n = 0n;
68
+ for (let i = 0; i < value.length; i++) {
69
+ const ch = value[i];
70
+ const v = BASE58_MAP[ch];
71
+ if (v === undefined) {
72
+ throw new UuidV7Error(`invalid base58 character: '${ch}'`);
73
+ }
74
+ n = n * 58n + BigInt(v);
75
+ }
76
+ const raw = [];
77
+ while (n > 0n) {
78
+ raw.unshift(Number(n & 0xffn));
79
+ n >>= 8n;
80
+ }
81
+ const padded = new Uint8Array(zeros + raw.length);
82
+ padded.set(raw, zeros);
83
+ if (padded.length !== UUID_BYTES_LEN) {
84
+ throw new UuidV7Error(`id must decode to ${UUID_BYTES_LEN} bytes`);
85
+ }
86
+ return padded;
87
+ }
88
+ function hexToBytes(hex) {
89
+ const out = new Uint8Array(hex.length / 2);
90
+ for (let i = 0; i < out.length; i++) {
91
+ out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
92
+ }
93
+ return out;
94
+ }
95
+ /**
96
+ * Mint a fresh ID. Callers should generate one of these locally when
97
+ * creating a new channel / thread / comment / conversation / message /
98
+ * group — the backend requires the client to supply the ID on create.
99
+ */
100
+ export function generateId() {
101
+ const hex = uuidv7().replace(/-/g, '');
102
+ return encodeUuidToBase58(hexToBytes(hex));
103
+ }
104
+ /**
105
+ * Resolve the `id` for a create-style API call: validate the caller-supplied
106
+ * value (throwing {@link UuidV7Error} before the request leaves the SDK) or
107
+ * mint a fresh one via {@link generateId}.
108
+ */
109
+ export function resolveCreateId(id) {
110
+ if (id === undefined)
111
+ return generateId();
112
+ if (!isValidUuidV7Base58(id)) {
113
+ throw new UuidV7Error(`invalid id ${JSON.stringify(id)} — use generateId() or omit \`id\` and let the SDK mint one.`);
114
+ }
115
+ return id;
116
+ }
117
+ /**
118
+ * Validate that a value matches the expected ID format (the decoded bytes
119
+ * have the v7 version nibble + RFC 4122/9562 variant bits). Does NOT
120
+ * validate the embedded timestamp — the backend may still reject a value
121
+ * that is too far in the future or past.
122
+ */
123
+ export function isValidUuidV7Base58(value) {
124
+ if (typeof value !== 'string')
125
+ return false;
126
+ let bytes;
127
+ try {
128
+ bytes = decodeBase58ToUuidBytes(value);
129
+ }
130
+ catch {
131
+ return false;
132
+ }
133
+ const versionNibble = bytes[6] & 0xf0;
134
+ const variantBits = bytes[8] & 0xc0;
135
+ return versionNibble === 0x70 && variantBits === 0x80;
136
+ }
137
+ /**
138
+ * Encode a canonical UUID string (hyphenated or not, any case) as a
139
+ * wire-format ID. Useful when interoperating with systems that hand you
140
+ * UUIDs in canonical form.
141
+ */
142
+ export function base58FromUuidString(uuid) {
143
+ const stripped = uuid.replace(/-/g, '').toLowerCase();
144
+ if (stripped.length !== 32 || !HEX_RE.test(stripped)) {
145
+ throw new UuidV7Error('not a valid UUID string');
146
+ }
147
+ return encodeUuidToBase58(hexToBytes(stripped));
148
+ }
149
+ /**
150
+ * Inverse of {@link base58FromUuidString}: takes a wire-format ID and
151
+ * returns the canonical hyphenated UUID string.
152
+ */
153
+ export function uuidStringFromBase58(value) {
154
+ const bytes = decodeBase58ToUuidBytes(value);
155
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
156
+ return [
157
+ hex.slice(0, 8),
158
+ hex.slice(8, 12),
159
+ hex.slice(12, 16),
160
+ hex.slice(16, 20),
161
+ hex.slice(20, 32),
162
+ ].join('-');
163
+ }
@@ -0,0 +1,160 @@
1
+ import type { CustomFetch } from './types/http.js';
2
+ export type AuthOptions = {
3
+ /** Optional custom base URL for OAuth endpoints */
4
+ baseUrl?: string;
5
+ /** Optional custom fetch implementation for cross-platform compatibility */
6
+ customFetch?: CustomFetch;
7
+ };
8
+ /**
9
+ * OAuth scopes for the Comms API.
10
+ *
11
+ * @remarks
12
+ * Request only the scopes your application needs:
13
+ *
14
+ * **User Scopes:**
15
+ * - `user:read` - Access user's personal settings
16
+ * - `user:write` - Access and update user's personal settings
17
+ *
18
+ * **Workspace Scopes:**
19
+ * - `workspaces:read` - Access teams the user is part of
20
+ * - `workspaces:write` - Access and update teams the user is part of
21
+ *
22
+ * **Channel Scopes:**
23
+ * - `channels:read` - Access channels
24
+ * - `channels:write` - Access and update channels
25
+ * - `channels:remove` - Access, update, and delete channels
26
+ *
27
+ * **Thread Scopes:**
28
+ * - `threads:read` - Access threads
29
+ * - `threads:write` - Access and update threads
30
+ * - `threads:remove` - Access, update, and delete threads
31
+ *
32
+ * **Comment Scopes:**
33
+ * - `comments:read` - Access comments
34
+ * - `comments:write` - Access and update comments
35
+ * - `comments:remove` - Access, update, and delete comments
36
+ *
37
+ * **Group Scopes:**
38
+ * - `groups:read` - Access groups
39
+ * - `groups:write` - Access and update groups
40
+ * - `groups:remove` - Access, update, and delete groups
41
+ *
42
+ * **Message Scopes:**
43
+ * - `messages:read` - Access messages
44
+ * - `messages:write` - Access and update messages
45
+ * - `messages:remove` - Access, update, and delete messages
46
+ *
47
+ * **Reaction Scopes:**
48
+ * - `reactions:read` - Access reactions
49
+ * - `reactions:write` - Access and update reactions
50
+ * - `reactions:remove` - Access, update, and delete reactions
51
+ *
52
+ * **Search Scopes:**
53
+ * - `search:read` - Search
54
+ *
55
+ * **Attachment Scopes:**
56
+ * - `attachments:read` - Access attachments
57
+ * - `attachments:write` - Access and update attachments
58
+ *
59
+ * **Notification Scopes:**
60
+ * - `notifications:read` - Read user's notifications settings
61
+ * - `notifications:write` - Read and update user's notifications settings
62
+ */
63
+ export declare const COMMS_SCOPES: readonly ["user:read", "user:write", "workspaces:read", "workspaces:write", "channels:read", "channels:write", "channels:remove", "threads:read", "threads:write", "threads:remove", "comments:read", "comments:write", "comments:remove", "groups:read", "groups:write", "groups:remove", "messages:read", "messages:write", "messages:remove", "reactions:read", "reactions:write", "reactions:remove", "search:read", "attachments:read", "attachments:write", "notifications:read", "notifications:write"];
64
+ /**
65
+ * Scopes determine what access a token has to the Comms API.
66
+ */
67
+ export type CommsScope = (typeof COMMS_SCOPES)[number];
68
+ export type AuthTokenRequestArgs = {
69
+ clientId: string;
70
+ clientSecret: string;
71
+ code: string;
72
+ redirectUri?: string;
73
+ };
74
+ export type AuthTokenResponse = {
75
+ accessToken: string;
76
+ tokenType: string;
77
+ refreshToken?: string;
78
+ expiresIn?: number;
79
+ scope?: string;
80
+ };
81
+ export type RevokeAuthTokenRequestArgs = {
82
+ clientId: string;
83
+ clientSecret: string;
84
+ accessToken: string;
85
+ };
86
+ /** Supported token endpoint authentication methods for dynamic client registration. */
87
+ export declare const TOKEN_ENDPOINT_AUTH_METHODS: readonly ["client_secret_post", "client_secret_basic", "none"];
88
+ /**
89
+ * Authentication method used at the token endpoint.
90
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-2 RFC 7591 Section 2}
91
+ */
92
+ export type TokenEndpointAuthMethod = (typeof TOKEN_ENDPOINT_AUTH_METHODS)[number];
93
+ /**
94
+ * Parameters for registering a new OAuth client via Dynamic Client Registration.
95
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591 RFC 7591}
96
+ */
97
+ export type ClientRegistrationRequest = {
98
+ redirectUris: string[];
99
+ clientName?: string;
100
+ clientUri?: string;
101
+ logoUri?: string;
102
+ scope?: readonly CommsScope[];
103
+ grantTypes?: string[];
104
+ responseTypes?: string[];
105
+ tokenEndpointAuthMethod?: TokenEndpointAuthMethod;
106
+ };
107
+ type RawClientRegistrationResponse = {
108
+ clientId: string;
109
+ clientSecret?: string;
110
+ clientName: string;
111
+ redirectUris: string[];
112
+ scope?: string;
113
+ grantTypes: string[];
114
+ responseTypes: string[];
115
+ tokenEndpointAuthMethod: string;
116
+ clientIdIssuedAt?: number;
117
+ clientSecretExpiresAt?: number;
118
+ clientUri?: string;
119
+ logoUri?: string;
120
+ };
121
+ /**
122
+ * Response from a successful dynamic client registration.
123
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 RFC 7591 Section 3.2.1}
124
+ */
125
+ export type ClientRegistrationResponse = Omit<RawClientRegistrationResponse, 'clientIdIssuedAt' | 'clientSecretExpiresAt' | 'scope'> & {
126
+ scope?: CommsScope[];
127
+ clientIdIssuedAt?: Date;
128
+ /** `null` indicates the client secret never expires. Absent when no secret is issued. */
129
+ clientSecretExpiresAt?: Date | null;
130
+ };
131
+ export declare function getAuthStateParameter(): string;
132
+ /**
133
+ * Generates the authorization URL for the OAuth2 flow.
134
+ *
135
+ * The `clientId` can be either a traditional client ID string (e.g. from
136
+ * {@link registerClient}) or an HTTPS URL pointing to a client metadata document,
137
+ * as defined in {@link https://drafts.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/ RFC draft-ietf-oauth-client-id-metadata-document}.
138
+ */
139
+ export declare function getAuthorizationUrl(clientId: string, scopes: CommsScope[], state: string, redirectUri?: string, baseUrl?: string): string;
140
+ export declare function getAuthToken(args: AuthTokenRequestArgs, options?: AuthOptions): Promise<AuthTokenResponse>;
141
+ export declare function revokeAuthToken(args: RevokeAuthTokenRequestArgs, options?: AuthOptions): Promise<boolean>;
142
+ /**
143
+ * Registers a new OAuth client via Dynamic Client Registration (RFC 7591).
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const client = await registerClient({
148
+ * redirectUris: ['https://example.com/callback'],
149
+ * clientName: 'My App',
150
+ * scope: ['user:read', 'channels:read'],
151
+ * })
152
+ * // Use client.clientId and client.clientSecret for OAuth flows
153
+ * ```
154
+ *
155
+ * @returns The registered client details
156
+ * @throws {@link CommsRequestError} If the registration fails
157
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591 RFC 7591}
158
+ */
159
+ export declare function registerClient(args: ClientRegistrationRequest, options?: AuthOptions): Promise<ClientRegistrationResponse>;
160
+ export {};
@@ -0,0 +1,12 @@
1
+ import { type Comment } from '../types/entities.js';
2
+ import type { CustomFetch } from '../types/http.js';
3
+ import type { CreateCommentArgs, ThreadAction } from '../types/requests.js';
4
+ type ClientContext = {
5
+ baseUri: string;
6
+ apiToken: string;
7
+ customFetch?: CustomFetch;
8
+ };
9
+ export declare function addCommentRequest(context: ClientContext, params: CreateCommentArgs, options?: {
10
+ threadAction?: ThreadAction;
11
+ }): Promise<Comment>;
12
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { CustomFetch } from '../types/http.js';
2
+ export type ClientConfig = {
3
+ /** API token for authentication */
4
+ apiToken: string;
5
+ /** Optional custom base URL. If not provided, uses the default Comms API URL */
6
+ baseUrl?: string;
7
+ /** Optional custom fetch implementation for cross-platform compatibility */
8
+ customFetch?: CustomFetch;
9
+ };
10
+ /**
11
+ * Base class for every Comms API client. Centralizes URL handling and
12
+ * config so individual clients stay focused on their endpoints.
13
+ */
14
+ export declare class BaseClient {
15
+ protected readonly apiToken: string;
16
+ protected readonly baseUrl?: string;
17
+ protected readonly customFetch?: CustomFetch;
18
+ constructor(config: ClientConfig);
19
+ /**
20
+ * Returns the base URI for an API request, with a guaranteed trailing
21
+ * slash so relative paths resolve cleanly through `URL`.
22
+ */
23
+ protected getBaseUri(): string;
24
+ }
@@ -0,0 +1,91 @@
1
+ import { z } from 'zod';
2
+ import { type Channel, type StatusOk } from '../types/entities.js';
3
+ import type { AddChannelUserArgs, AddChannelUsersArgs, CreateChannelArgs, GetChannelsArgs, RemoveChannelUserArgs, RemoveChannelUsersArgs, UpdateChannelArgs } from '../types/requests.js';
4
+ import { BaseClient } from './base-client.js';
5
+ export declare const ChannelListSchema: z.ZodArray<z.ZodPipe<z.ZodObject<{
6
+ id: z.ZodString;
7
+ name: z.ZodString;
8
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
9
+ creator: z.ZodNumber;
10
+ userIds: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber>>>;
11
+ color: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
12
+ public: z.ZodBoolean;
13
+ workspaceId: z.ZodNumber;
14
+ archived: z.ZodBoolean;
15
+ created: z.ZodDate;
16
+ useDefaultRecipients: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
17
+ defaultGroups: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
18
+ defaultRecipients: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber>>>;
19
+ isFavorited: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
20
+ icon: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
21
+ version: z.ZodNumber;
22
+ filters: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>>;
23
+ }, z.core.$strip>, z.ZodTransform<{
24
+ url: string;
25
+ id: string;
26
+ name: string;
27
+ creator: number;
28
+ public: boolean;
29
+ workspaceId: number;
30
+ archived: boolean;
31
+ created: Date;
32
+ version: number;
33
+ description?: string | null | undefined;
34
+ userIds?: number[] | null | undefined;
35
+ color?: number | null | undefined;
36
+ useDefaultRecipients?: boolean | null | undefined;
37
+ defaultGroups?: string[] | null | undefined;
38
+ defaultRecipients?: number[] | null | undefined;
39
+ isFavorited?: boolean | null | undefined;
40
+ icon?: number | null | undefined;
41
+ filters?: Record<string, string> | null | undefined;
42
+ }, {
43
+ id: string;
44
+ name: string;
45
+ creator: number;
46
+ public: boolean;
47
+ workspaceId: number;
48
+ archived: boolean;
49
+ created: Date;
50
+ version: number;
51
+ description?: string | null | undefined;
52
+ userIds?: number[] | null | undefined;
53
+ color?: number | null | undefined;
54
+ useDefaultRecipients?: boolean | null | undefined;
55
+ defaultGroups?: string[] | null | undefined;
56
+ defaultRecipients?: number[] | null | undefined;
57
+ isFavorited?: boolean | null | undefined;
58
+ icon?: number | null | undefined;
59
+ filters?: Record<string, string> | null | undefined;
60
+ }>>>;
61
+ /**
62
+ * Client for `/api/v1/channels/`. The SDK auto-generates an `id` on
63
+ * `createChannel` when the caller doesn't supply one — pass your own `id`
64
+ * to keep an optimistic-UI ID stable through the round-trip.
65
+ */
66
+ export declare class ChannelsClient extends BaseClient {
67
+ /** Lists channels in a workspace. */
68
+ getChannels(args: GetChannelsArgs): Promise<Channel[]>;
69
+ /** Fetches a single channel by ID. */
70
+ getChannel(id: string): Promise<Channel>;
71
+ /** Creates a new channel. `id` is auto-generated if not supplied. */
72
+ createChannel(args: CreateChannelArgs): Promise<Channel>;
73
+ /** Partial update of an existing channel. */
74
+ updateChannel(args: UpdateChannelArgs): Promise<Channel>;
75
+ /** Updates the channel's view filter (`only_open` / `all` / `only_closed`). */
76
+ updateFilters(args: {
77
+ id: string;
78
+ filterClosed: 'only_open' | 'all' | 'only_closed';
79
+ }): Promise<StatusOk>;
80
+ /** Permanently deletes a channel. */
81
+ deleteChannel(id: string): Promise<StatusOk>;
82
+ archiveChannel(id: string): Promise<StatusOk>;
83
+ unarchiveChannel(id: string): Promise<StatusOk>;
84
+ favoriteChannel(id: string): Promise<StatusOk>;
85
+ unfavoriteChannel(id: string): Promise<StatusOk>;
86
+ addUser(args: AddChannelUserArgs): Promise<Channel>;
87
+ addUsers(args: AddChannelUsersArgs): Promise<Channel>;
88
+ removeUser(args: RemoveChannelUserArgs): Promise<Channel>;
89
+ removeUsers(args: RemoveChannelUsersArgs): Promise<Channel>;
90
+ private simple;
91
+ }