@doist/comms-sdk 0.1.0-alpha.1 → 0.2.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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/clients/add-comment-helper.js +18 -2
  3. package/dist/cjs/clients/base-client.js +11 -5
  4. package/dist/cjs/clients/channels-client.js +142 -14
  5. package/dist/cjs/clients/comments-client.js +99 -14
  6. package/dist/cjs/clients/conversation-messages-client.js +91 -9
  7. package/dist/cjs/clients/conversations-client.js +166 -15
  8. package/dist/cjs/clients/groups-client.js +98 -5
  9. package/dist/cjs/clients/inbox-client.js +102 -16
  10. package/dist/cjs/clients/reactions-client.js +40 -2
  11. package/dist/cjs/clients/search-client.js +50 -0
  12. package/dist/cjs/clients/threads-client.js +238 -24
  13. package/dist/cjs/clients/users-client.js +138 -11
  14. package/dist/cjs/clients/workspace-users-client.js +110 -10
  15. package/dist/cjs/clients/workspaces-client.js +89 -8
  16. package/dist/cjs/comms-api.js +1 -0
  17. package/dist/cjs/consts/endpoints.js +8 -3
  18. package/dist/cjs/testUtils/test-defaults.js +3 -1
  19. package/dist/cjs/types/api-version.js +8 -0
  20. package/dist/cjs/types/entities.js +119 -98
  21. package/dist/cjs/types/index.js +1 -0
  22. package/dist/cjs/types/requests.js +0 -1
  23. package/dist/cjs/utils/url-helpers.js +3 -1
  24. package/dist/esm/clients/add-comment-helper.js +18 -2
  25. package/dist/esm/clients/base-client.js +11 -5
  26. package/dist/esm/clients/channels-client.js +143 -15
  27. package/dist/esm/clients/comments-client.js +100 -15
  28. package/dist/esm/clients/conversation-messages-client.js +92 -10
  29. package/dist/esm/clients/conversations-client.js +167 -16
  30. package/dist/esm/clients/groups-client.js +98 -5
  31. package/dist/esm/clients/inbox-client.js +102 -16
  32. package/dist/esm/clients/reactions-client.js +40 -2
  33. package/dist/esm/clients/search-client.js +50 -0
  34. package/dist/esm/clients/threads-client.js +239 -25
  35. package/dist/esm/clients/users-client.js +138 -11
  36. package/dist/esm/clients/workspace-users-client.js +110 -10
  37. package/dist/esm/clients/workspaces-client.js +90 -9
  38. package/dist/esm/comms-api.js +1 -0
  39. package/dist/esm/consts/endpoints.js +8 -3
  40. package/dist/esm/testUtils/test-defaults.js +2 -0
  41. package/dist/esm/types/api-version.js +5 -0
  42. package/dist/esm/types/entities.js +111 -97
  43. package/dist/esm/types/index.js +1 -0
  44. package/dist/esm/types/requests.js +0 -1
  45. package/dist/esm/utils/url-helpers.js +3 -1
  46. package/dist/types/clients/add-comment-helper.d.ts +20 -1
  47. package/dist/types/clients/base-client.d.ts +10 -0
  48. package/dist/types/clients/channels-client.d.ts +126 -6
  49. package/dist/types/clients/comments-client.d.ts +77 -6
  50. package/dist/types/clients/conversation-messages-client.d.ts +79 -5
  51. package/dist/types/clients/conversations-client.d.ts +146 -3
  52. package/dist/types/clients/groups-client.d.ts +98 -5
  53. package/dist/types/clients/inbox-client.d.ts +463 -5
  54. package/dist/types/clients/reactions-client.d.ts +40 -2
  55. package/dist/types/clients/search-client.d.ts +50 -0
  56. package/dist/types/clients/threads-client.d.ts +204 -8
  57. package/dist/types/clients/users-client.d.ts +138 -11
  58. package/dist/types/clients/workspace-users-client.d.ts +110 -10
  59. package/dist/types/clients/workspaces-client.d.ts +82 -7
  60. package/dist/types/comms-api.d.ts +3 -0
  61. package/dist/types/consts/endpoints.d.ts +6 -1
  62. package/dist/types/testUtils/test-defaults.d.ts +1 -0
  63. package/dist/types/types/api-version.d.ts +6 -0
  64. package/dist/types/types/entities.d.ts +1654 -126
  65. package/dist/types/types/index.d.ts +1 -0
  66. package/dist/types/types/requests.d.ts +2 -21
  67. package/package.json +1 -1
@@ -6,7 +6,20 @@ import { BaseClient } from './base-client.js';
6
6
  * rejects non-empty `name` and `channelIds` — set neither.
7
7
  */
8
8
  export class WorkspaceUsersClient extends BaseClient {
9
- /** Returns workspace user objects for the given workspace id. */
9
+ /**
10
+ * Returns a list of workspace user objects for the given workspace id.
11
+ *
12
+ * @param args - The arguments for getting workspace users.
13
+ * @param args.workspaceId - The workspace ID.
14
+ * @param args.archived - Optional flag to filter archived users.
15
+ * @returns An array of workspace user objects.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const users = await api.workspaceUsers.getWorkspaceUsers({ workspaceId: 123 })
20
+ * users.forEach(u => console.log(u.fullName, u.userType))
21
+ * ```
22
+ */
10
23
  getWorkspaceUsers(args) {
11
24
  return request({
12
25
  httpMethod: 'GET',
@@ -17,7 +30,12 @@ export class WorkspaceUsersClient extends BaseClient {
17
30
  customFetch: this.customFetch,
18
31
  }).then((response) => response.data.map((user) => WorkspaceUserSchema.parse(user)));
19
32
  }
20
- /** Returns workspace user IDs for the given workspace id. */
33
+ /**
34
+ * Returns a list of workspace user IDs for the given workspace id.
35
+ *
36
+ * @param workspaceId - The workspace ID.
37
+ * @returns An array of user IDs.
38
+ */
21
39
  getWorkspaceUserIds(workspaceId) {
22
40
  return request({
23
41
  httpMethod: 'GET',
@@ -28,7 +46,20 @@ export class WorkspaceUsersClient extends BaseClient {
28
46
  customFetch: this.customFetch,
29
47
  }).then((response) => response.data);
30
48
  }
31
- /** Gets a user by id. */
49
+ /**
50
+ * Gets a user by id.
51
+ *
52
+ * @param args - The arguments for getting a user by ID.
53
+ * @param args.workspaceId - The workspace ID.
54
+ * @param args.userId - The user's ID.
55
+ * @returns The workspace user object.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const user = await api.workspaceUsers.getUserById({ workspaceId: 123, userId: 456 })
60
+ * console.log(user.fullName, user.email)
61
+ * ```
62
+ */
32
63
  getUserById(args) {
33
64
  return request({
34
65
  httpMethod: 'GET',
@@ -39,7 +70,22 @@ export class WorkspaceUsersClient extends BaseClient {
39
70
  customFetch: this.customFetch,
40
71
  }).then((response) => WorkspaceUserSchema.parse(response.data));
41
72
  }
42
- /** Gets a user by email. */
73
+ /**
74
+ * Gets a user by email.
75
+ *
76
+ * @param args - The arguments for getting a user by email.
77
+ * @param args.workspaceId - The workspace ID.
78
+ * @param args.email - The user's email.
79
+ * @returns The workspace user object.
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const user = await api.workspaceUsers.getUserByEmail({
84
+ * workspaceId: 123,
85
+ * email: 'user@example.com',
86
+ * })
87
+ * ```
88
+ */
43
89
  getUserByEmail(args) {
44
90
  return request({
45
91
  httpMethod: 'GET',
@@ -50,7 +96,14 @@ export class WorkspaceUsersClient extends BaseClient {
50
96
  customFetch: this.customFetch,
51
97
  }).then((response) => WorkspaceUserSchema.parse(response.data));
52
98
  }
53
- /** Gets the user's info in the context of the workspace. */
99
+ /**
100
+ * Gets the user's info in the context of the workspace.
101
+ *
102
+ * @param args - The arguments for getting user info.
103
+ * @param args.workspaceId - The workspace ID.
104
+ * @param args.userId - The user's ID.
105
+ * @returns Information about the user in the workspace context.
106
+ */
54
107
  getUserInfo(args) {
55
108
  return request({
56
109
  httpMethod: 'GET',
@@ -61,7 +114,23 @@ export class WorkspaceUsersClient extends BaseClient {
61
114
  customFetch: this.customFetch,
62
115
  }).then((response) => response.data);
63
116
  }
64
- /** Gets the user's local time (e.g., "2017-05-10 07:55:40"). */
117
+ /**
118
+ * Gets the user's local time (e.g., "2017-05-10 07:55:40").
119
+ *
120
+ * @param args - The arguments for getting user local time.
121
+ * @param args.workspaceId - The workspace ID.
122
+ * @param args.userId - The user's ID.
123
+ * @returns The user's local time as a string.
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const localTime = await api.workspaceUsers.getUserLocalTime({
128
+ * workspaceId: 123,
129
+ * userId: 456,
130
+ * })
131
+ * console.log('User local time:', localTime)
132
+ * ```
133
+ */
65
134
  getUserLocalTime(args) {
66
135
  return request({
67
136
  httpMethod: 'GET',
@@ -72,7 +141,15 @@ export class WorkspaceUsersClient extends BaseClient {
72
141
  customFetch: this.customFetch,
73
142
  }).then((response) => response.data);
74
143
  }
75
- /** Adds a person to a workspace. */
144
+ /**
145
+ * Adds a person to a workspace.
146
+ *
147
+ * @param args - The arguments for adding a user.
148
+ * @param args.workspaceId - The workspace ID.
149
+ * @param args.email - The user's email.
150
+ * @param args.userType - Optional user type (USER, GUEST, or ADMIN).
151
+ * @returns The created workspace user object.
152
+ */
76
153
  addUser(args) {
77
154
  return request({
78
155
  httpMethod: 'POST',
@@ -87,7 +164,16 @@ export class WorkspaceUsersClient extends BaseClient {
87
164
  customFetch: this.customFetch,
88
165
  }).then((response) => WorkspaceUserSchema.parse(response.data));
89
166
  }
90
- /** Updates a person in a workspace. */
167
+ /**
168
+ * Updates a person in a workspace.
169
+ *
170
+ * @param args - The arguments for updating a user.
171
+ * @param args.workspaceId - The workspace ID.
172
+ * @param args.userType - The user type (USER, GUEST, or ADMIN).
173
+ * @param args.email - Optional email of the user to update.
174
+ * @param args.userId - Optional user ID to update (use either email or userId).
175
+ * @returns The updated workspace user object.
176
+ */
91
177
  updateUser(args) {
92
178
  return request({
93
179
  httpMethod: 'POST',
@@ -103,7 +189,14 @@ export class WorkspaceUsersClient extends BaseClient {
103
189
  customFetch: this.customFetch,
104
190
  }).then((response) => WorkspaceUserSchema.parse(response.data));
105
191
  }
106
- /** Removes a person from a workspace. */
192
+ /**
193
+ * Removes a person from a workspace.
194
+ *
195
+ * @param args - The arguments for removing a user.
196
+ * @param args.workspaceId - The workspace ID.
197
+ * @param args.email - Optional email of the user to remove.
198
+ * @param args.userId - Optional user ID to remove (use either email or userId).
199
+ */
107
200
  removeUser(args) {
108
201
  return request({
109
202
  httpMethod: 'POST',
@@ -118,7 +211,14 @@ export class WorkspaceUsersClient extends BaseClient {
118
211
  customFetch: this.customFetch,
119
212
  }).then(() => undefined);
120
213
  }
121
- /** Sends a new workspace invitation to the selected user. */
214
+ /**
215
+ * Sends a new workspace invitation to the selected user.
216
+ *
217
+ * @param args - The arguments for resending an invite.
218
+ * @param args.workspaceId - The workspace ID.
219
+ * @param args.email - The user's email.
220
+ * @param args.userId - Optional user ID.
221
+ */
122
222
  resendInvite(args) {
123
223
  return request({
124
224
  httpMethod: 'POST',
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { ENDPOINT_WORKSPACES } from '../consts/endpoints.js';
3
3
  import { request } from '../transport/http-client.js';
4
- import { ChannelSchema, WorkspaceSchema } from '../types/entities.js';
4
+ import { ChannelSchema, createChannelSchema, WorkspaceSchema, } from '../types/entities.js';
5
5
  import { BaseClient } from './base-client.js';
6
6
  export const ChannelListSchema = z.array(ChannelSchema);
7
7
  /**
@@ -9,7 +9,25 @@ export const ChannelListSchema = z.array(ChannelSchema);
9
9
  * currently rejects any `color` other than `1` on add/update.
10
10
  */
11
11
  export class WorkspacesClient extends BaseClient {
12
- /** Gets all the user's workspaces. */
12
+ constructor() {
13
+ super(...arguments);
14
+ this.linkBaseUrl = this.getLinkBaseUrl();
15
+ // Reuse the shared singleton when no custom base is configured.
16
+ this.channelListSchema = this.linkBaseUrl
17
+ ? z.array(createChannelSchema(this.linkBaseUrl))
18
+ : ChannelListSchema;
19
+ }
20
+ /**
21
+ * Gets all the user's workspaces.
22
+ *
23
+ * @returns An array of all workspaces the user belongs to.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const workspaces = await api.workspaces.getWorkspaces()
28
+ * workspaces.forEach(ws => console.log(ws.name))
29
+ * ```
30
+ */
13
31
  getWorkspaces() {
14
32
  return request({
15
33
  httpMethod: 'GET',
@@ -20,7 +38,18 @@ export class WorkspacesClient extends BaseClient {
20
38
  customFetch: this.customFetch,
21
39
  }).then((response) => response.data.map((workspace) => WorkspaceSchema.parse(workspace)));
22
40
  }
23
- /** Gets a single workspace object by id. */
41
+ /**
42
+ * Gets a single workspace object by id.
43
+ *
44
+ * @param id - The workspace ID.
45
+ * @returns The workspace object.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const workspace = await api.workspaces.getWorkspace(123)
50
+ * console.log(workspace.name)
51
+ * ```
52
+ */
24
53
  getWorkspace(id) {
25
54
  return request({
26
55
  httpMethod: 'GET',
@@ -31,7 +60,17 @@ export class WorkspacesClient extends BaseClient {
31
60
  customFetch: this.customFetch,
32
61
  }).then((response) => WorkspaceSchema.parse(response.data));
33
62
  }
34
- /** Gets the user's default workspace. */
63
+ /**
64
+ * Gets the user's default workspace.
65
+ *
66
+ * @returns The default workspace object.
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * const workspace = await api.workspaces.getDefaultWorkspace()
71
+ * console.log(workspace.name)
72
+ * ```
73
+ */
35
74
  getDefaultWorkspace() {
36
75
  return request({
37
76
  httpMethod: 'GET',
@@ -42,7 +81,18 @@ export class WorkspacesClient extends BaseClient {
42
81
  customFetch: this.customFetch,
43
82
  }).then((response) => WorkspaceSchema.parse(response.data));
44
83
  }
45
- /** Creates a new workspace. */
84
+ /**
85
+ * Creates a new workspace.
86
+ *
87
+ * @param name - The name of the new workspace.
88
+ * @returns The created workspace object.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const workspace = await api.workspaces.createWorkspace('My Team')
93
+ * console.log('Created:', workspace.name)
94
+ * ```
95
+ */
46
96
  createWorkspace(name) {
47
97
  return request({
48
98
  httpMethod: 'POST',
@@ -53,7 +103,18 @@ export class WorkspacesClient extends BaseClient {
53
103
  customFetch: this.customFetch,
54
104
  }).then((response) => WorkspaceSchema.parse(response.data));
55
105
  }
56
- /** Updates an existing workspace. */
106
+ /**
107
+ * Updates an existing workspace.
108
+ *
109
+ * @param id - The workspace ID.
110
+ * @param name - The new name for the workspace.
111
+ * @returns The updated workspace object.
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * const workspace = await api.workspaces.updateWorkspace(123, 'New Team Name')
116
+ * ```
117
+ */
57
118
  updateWorkspace(id, name) {
58
119
  return request({
59
120
  httpMethod: 'POST',
@@ -64,7 +125,16 @@ export class WorkspacesClient extends BaseClient {
64
125
  customFetch: this.customFetch,
65
126
  }).then((response) => WorkspaceSchema.parse(response.data));
66
127
  }
67
- /** Removes a workspace and all its data (not recoverable). */
128
+ /**
129
+ * Removes a workspace and all its data (not recoverable).
130
+ *
131
+ * @param id - The workspace ID.
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * await api.workspaces.removeWorkspace(123)
136
+ * ```
137
+ */
68
138
  removeWorkspace(id) {
69
139
  return request({
70
140
  httpMethod: 'POST',
@@ -75,7 +145,18 @@ export class WorkspacesClient extends BaseClient {
75
145
  customFetch: this.customFetch,
76
146
  }).then(() => undefined);
77
147
  }
78
- /** Gets the public channels of a workspace. */
148
+ /**
149
+ * Gets the public channels of a workspace.
150
+ *
151
+ * @param id - The workspace ID.
152
+ * @returns An array of public channel objects.
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * const channels = await api.workspaces.getPublicChannels(123)
157
+ * channels.forEach(ch => console.log(ch.name))
158
+ * ```
159
+ */
79
160
  getPublicChannels(id) {
80
161
  return request({
81
162
  httpMethod: 'GET',
@@ -84,6 +165,6 @@ export class WorkspacesClient extends BaseClient {
84
165
  apiToken: this.apiToken,
85
166
  payload: { id },
86
167
  customFetch: this.customFetch,
87
- }).then((response) => ChannelListSchema.parse(response.data));
168
+ }).then((response) => this.channelListSchema.parse(response.data));
88
169
  }
89
170
  }
@@ -33,6 +33,7 @@ export class CommsApi {
33
33
  const clientConfig = {
34
34
  apiToken: authToken,
35
35
  baseUrl: options?.baseUrl,
36
+ version: options?.version,
36
37
  customFetch: options?.customFetch,
37
38
  };
38
39
  this.users = new UsersClient(clientConfig);
@@ -1,13 +1,18 @@
1
+ import { DEFAULT_API_VERSION } from '../types/api-version.js';
1
2
  const BASE_URI = 'https://comms.todoist.com';
2
- const API_VERSION = 'v1';
3
3
  /**
4
4
  * Gets the base URI for Comms API requests.
5
5
  *
6
+ * Preserves any path component on `domainBase` so callers can route through
7
+ * a proxy (e.g. `https://proxy.example.com/comms` → `.../comms/api/v1/`).
8
+ *
9
+ * @param version - API version. Defaults to 'v1'.
6
10
  * @param domainBase - Custom domain base URL. Defaults to Comms' API domain.
7
11
  * @returns Complete base URI with trailing slash (e.g., 'https://comms.todoist.com/api/v1/')
8
12
  */
9
- export function getCommsBaseUri(domainBase = BASE_URI) {
10
- return new URL(`/api/${API_VERSION}/`, domainBase).toString();
13
+ export function getCommsBaseUri(version = DEFAULT_API_VERSION, domainBase = BASE_URI) {
14
+ const base = domainBase.endsWith('/') ? domainBase : `${domainBase}/`;
15
+ return new URL(`api/${version}/`, base).toString();
11
16
  }
12
17
  export const ENDPOINT_USERS = 'users';
13
18
  export const ENDPOINT_WORKSPACES = 'workspaces';
@@ -1,4 +1,6 @@
1
+ import { getCommsBaseUri } from '../consts/endpoints.js';
1
2
  export const TEST_API_TOKEN = 'test-api-token';
3
+ export const TEST_API_BASE_URL = getCommsBaseUri().replace(/\/$/, '');
2
4
  // Canonical test IDs — shaped so they pass SDK-side validation.
3
5
  export const TEST_CHANNEL_ID = '7YpL3oZ4kZ9vP7Q1tR2sX3y';
4
6
  export const TEST_THREAD_ID = '7YpL3oZ4kZ9vP7Q1tR2sX3z';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Supported Comms API versions
3
+ */
4
+ export const API_VERSIONS = ['v1'];
5
+ export const DEFAULT_API_VERSION = 'v1';
@@ -97,8 +97,7 @@ export const WorkspaceSchema = z.object({
97
97
  .optional(),
98
98
  plan: z.string().nullable().optional(),
99
99
  });
100
- export const ChannelSchema = z
101
- .object({
100
+ export const ChannelObjectSchema = z.object({
102
101
  id: z.string(),
103
102
  name: z.string(),
104
103
  description: z.string().nullable().optional(),
@@ -116,15 +115,17 @@ export const ChannelSchema = z
116
115
  icon: z.number().nullable().optional(),
117
116
  version: z.number(),
118
117
  filters: z.record(z.string(), z.string()).nullable().optional(),
119
- })
120
- .transform((data) => ({
121
- ...data,
122
- url: getFullCommsURL({ workspaceId: data.workspaceId, channelId: data.id }),
123
- }));
118
+ });
119
+ export function createChannelSchema(linkBaseUrl) {
120
+ return ChannelObjectSchema.transform((data) => ({
121
+ ...data,
122
+ url: getFullCommsURL({ workspaceId: data.workspaceId, channelId: data.id }, linkBaseUrl),
123
+ }));
124
+ }
125
+ export const ChannelSchema = createChannelSchema();
124
126
  // Thread entity from API. `pinned` (boolean) and `pinnedTs` (epoch ms or
125
127
  // null) are both surfaced — `pinned` is kept for Zapier/webhook clients.
126
- export const ThreadSchema = z
127
- .object({
128
+ export const ThreadObjectSchema = z.object({
128
129
  id: z.string(),
129
130
  title: z.string(),
130
131
  content: z.string(),
@@ -188,15 +189,18 @@ export const ThreadSchema = z
188
189
  })
189
190
  .nullable()
190
191
  .optional(),
191
- })
192
- .transform((data) => ({
193
- ...data,
194
- url: getFullCommsURL({
195
- workspaceId: data.workspaceId,
196
- channelId: data.channelId,
197
- threadId: data.id,
198
- }),
199
- }));
192
+ });
193
+ export function createThreadSchema(linkBaseUrl) {
194
+ return ThreadObjectSchema.transform((data) => ({
195
+ ...data,
196
+ url: getFullCommsURL({
197
+ workspaceId: data.workspaceId,
198
+ channelId: data.channelId,
199
+ threadId: data.id,
200
+ }, linkBaseUrl),
201
+ }));
202
+ }
203
+ export const ThreadSchema = createThreadSchema();
200
204
  // Group entity from API. The broadcast markers `EVERYONE` and
201
205
  // `EVERYONE_IN_THREAD` can appear in place of a real `id` in group-bearing
202
206
  // fields on threads/comments/channels.
@@ -209,8 +213,7 @@ export const GroupSchema = z.object({
209
213
  version: z.number(),
210
214
  });
211
215
  // Conversation entity from API.
212
- export const ConversationSchema = z
213
- .object({
216
+ export const ConversationObjectSchema = z.object({
214
217
  id: z.string(),
215
218
  workspaceId: z.number(),
216
219
  userIds: z.array(z.number()),
@@ -247,13 +250,15 @@ export const ConversationSchema = z
247
250
  })
248
251
  .nullable()
249
252
  .optional(),
250
- })
251
- .transform((data) => ({
252
- ...data,
253
- url: getFullCommsURL({ workspaceId: data.workspaceId, conversationId: data.id }),
254
- }));
255
- export const CommentSchema = z
256
- .object({
253
+ });
254
+ export function createConversationSchema(linkBaseUrl) {
255
+ return ConversationObjectSchema.transform((data) => ({
256
+ ...data,
257
+ url: getFullCommsURL({ workspaceId: data.workspaceId, conversationId: data.id }, linkBaseUrl),
258
+ }));
259
+ }
260
+ export const ConversationSchema = createConversationSchema();
261
+ export const CommentObjectSchema = z.object({
257
262
  id: z.string(),
258
263
  content: z.string(),
259
264
  creator: z.number(),
@@ -277,16 +282,19 @@ export const CommentSchema = z
277
282
  deletedBy: z.number().nullable().optional(),
278
283
  version: z.number().nullable().optional(),
279
284
  actions: z.array(z.unknown()).nullable().optional(),
280
- })
281
- .transform((data) => ({
282
- ...data,
283
- url: getFullCommsURL({
284
- workspaceId: data.workspaceId,
285
- channelId: data.channelId,
286
- threadId: data.threadId,
287
- commentId: data.id,
288
- }),
289
- }));
285
+ });
286
+ export function createCommentSchema(linkBaseUrl) {
287
+ return CommentObjectSchema.transform((data) => ({
288
+ ...data,
289
+ url: getFullCommsURL({
290
+ workspaceId: data.workspaceId,
291
+ channelId: data.channelId,
292
+ threadId: data.threadId,
293
+ commentId: data.id,
294
+ }, linkBaseUrl),
295
+ }));
296
+ }
297
+ export const CommentSchema = createCommentSchema();
290
298
  export const WorkspaceUserSchema = BaseUserSchema.extend({
291
299
  email: z.string().nullable().optional(),
292
300
  userType: z.enum(USER_TYPES),
@@ -299,8 +307,7 @@ export const WorkspaceUserSchema = BaseUserSchema.extend({
299
307
  // (coerced to a string post-parse) because the backend currently emits
300
308
  // either shape depending on the endpoint; the URL/reaction helpers accept
301
309
  // both.
302
- export const ConversationMessageSchema = z
303
- .object({
310
+ export const ConversationMessageObjectSchema = z.object({
304
311
  id: z.union([z.string(), z.number()]).transform(String),
305
312
  content: z.string(),
306
313
  creator: z.number(),
@@ -317,65 +324,72 @@ export const ConversationMessageSchema = z
317
324
  directMentions: z.array(z.number()).nullable().optional(),
318
325
  version: z.number().nullable().optional(),
319
326
  workspaceId: z.number(),
320
- })
321
- .transform((data) => ({
322
- ...data,
323
- url: getFullCommsURL({
324
- workspaceId: data.workspaceId,
325
- conversationId: data.conversationId,
326
- messageId: data.id,
327
- }),
328
- }));
327
+ });
328
+ export function createConversationMessageSchema(linkBaseUrl) {
329
+ return ConversationMessageObjectSchema.transform((data) => ({
330
+ ...data,
331
+ url: getFullCommsURL({
332
+ workspaceId: data.workspaceId,
333
+ conversationId: data.conversationId,
334
+ messageId: data.id,
335
+ }, linkBaseUrl),
336
+ }));
337
+ }
338
+ export const ConversationMessageSchema = createConversationMessageSchema();
329
339
  // InboxThread entity from API - returns full Thread objects with additional inbox metadata.
330
- export const InboxThreadSchema = z
331
- .object({
332
- id: z.string(),
333
- title: z.string(),
334
- content: z.string(),
335
- creator: z.number(),
336
- creatorName: z.string().nullable().optional(),
337
- channelId: z.string(),
338
- workspaceId: z.number(),
339
- actions: z.array(z.unknown()).nullable().optional(),
340
- attachments: z.array(AttachmentSchema).nullable().optional(),
341
- commentCount: z.number(),
342
- directGroupMentions: z.array(z.string()).nullable().optional(),
343
- directMentions: z.array(z.number()).nullable().optional(),
344
- groups: z.array(z.string()).nullable().optional(),
345
- lastEdited: z.date().nullable().optional(),
346
- lastObjIndex: z.number().nullable().optional(),
347
- lastUpdated: z.date(),
348
- mutedUntil: z.date().nullable().optional(),
349
- participants: z.array(z.number()).nullable().optional(),
350
- // Backend wire shape only includes `pinned_ts` (epoch ms or null);
351
- // derive `pinned` from `pinnedTs != null` if you need a bool.
352
- pinned: z.boolean().optional(),
353
- pinnedTs: z.number().int().nullable().optional(),
354
- posted: z.date(),
355
- reactions: z.record(z.string(), z.array(z.number())).nullable().optional(),
356
- recipients: z.array(z.number()).nullable().optional(),
357
- snippet: z.string(),
358
- snippetCreator: z.number(),
359
- snippetMaskAvatarUrl: z.string().nullable().optional(),
360
- snippetMaskPoster: z.string().nullable().optional(),
361
- systemMessage: SystemMessageSchema,
362
- isArchived: z.boolean(),
363
- inInbox: z.boolean(),
364
- isSaved: z.boolean().nullable().optional(),
365
- closed: z.boolean(),
366
- responders: z.array(z.number()).nullable().optional(),
367
- lastComment: CommentSchema.nullable().optional(),
368
- toEmails: z.array(z.string()).nullable().optional(),
369
- version: z.number().nullable().optional(),
370
- })
371
- .transform((data) => ({
372
- ...data,
373
- url: getFullCommsURL({
374
- workspaceId: data.workspaceId,
375
- channelId: data.channelId,
376
- threadId: data.id,
377
- }),
378
- }));
340
+ export function createInboxThreadObjectSchema(linkBaseUrl) {
341
+ return z.object({
342
+ id: z.string(),
343
+ title: z.string(),
344
+ content: z.string(),
345
+ creator: z.number(),
346
+ creatorName: z.string().nullable().optional(),
347
+ channelId: z.string(),
348
+ workspaceId: z.number(),
349
+ actions: z.array(z.unknown()).nullable().optional(),
350
+ attachments: z.array(AttachmentSchema).nullable().optional(),
351
+ commentCount: z.number(),
352
+ directGroupMentions: z.array(z.string()).nullable().optional(),
353
+ directMentions: z.array(z.number()).nullable().optional(),
354
+ groups: z.array(z.string()).nullable().optional(),
355
+ lastEdited: z.date().nullable().optional(),
356
+ lastObjIndex: z.number().nullable().optional(),
357
+ lastUpdated: z.date(),
358
+ mutedUntil: z.date().nullable().optional(),
359
+ participants: z.array(z.number()).nullable().optional(),
360
+ // Backend wire shape only includes `pinned_ts` (epoch ms or null);
361
+ // derive `pinned` from `pinnedTs != null` if you need a bool.
362
+ pinned: z.boolean().optional(),
363
+ pinnedTs: z.number().int().nullable().optional(),
364
+ posted: z.date(),
365
+ reactions: z.record(z.string(), z.array(z.number())).nullable().optional(),
366
+ recipients: z.array(z.number()).nullable().optional(),
367
+ snippet: z.string(),
368
+ snippetCreator: z.number(),
369
+ snippetMaskAvatarUrl: z.string().nullable().optional(),
370
+ snippetMaskPoster: z.string().nullable().optional(),
371
+ systemMessage: SystemMessageSchema,
372
+ isArchived: z.boolean(),
373
+ inInbox: z.boolean(),
374
+ isSaved: z.boolean().nullable().optional(),
375
+ closed: z.boolean(),
376
+ responders: z.array(z.number()).nullable().optional(),
377
+ lastComment: createCommentSchema(linkBaseUrl).nullable().optional(),
378
+ toEmails: z.array(z.string()).nullable().optional(),
379
+ version: z.number().nullable().optional(),
380
+ });
381
+ }
382
+ export function createInboxThreadSchema(linkBaseUrl) {
383
+ return createInboxThreadObjectSchema(linkBaseUrl).transform((data) => ({
384
+ ...data,
385
+ url: getFullCommsURL({
386
+ workspaceId: data.workspaceId,
387
+ channelId: data.channelId,
388
+ threadId: data.id,
389
+ }, linkBaseUrl),
390
+ }));
391
+ }
392
+ export const InboxThreadSchema = createInboxThreadSchema();
379
393
  // UnreadThread entity from API - simplified thread reference.
380
394
  export const UnreadThreadSchema = z.object({
381
395
  threadId: z.string(),
@@ -1,3 +1,4 @@
1
+ export * from './api-version.js';
1
2
  export * from './entities.js';
2
3
  export * from './enums.js';
3
4
  export * from './errors.js';
@@ -94,7 +94,6 @@ export const GetThreadsArgsSchema = z.object({
94
94
  });
95
95
  export const GetCommentsArgsSchema = z.object({
96
96
  threadId: z.string(),
97
- from: z.date().nullable().optional(),
98
97
  newerThan: z.date().nullable().optional(),
99
98
  olderThan: z.date().nullable().optional(),
100
99
  limit: z.number().nullable().optional(),