@doist/comms-sdk 0.10.0 → 0.11.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.
@@ -24,7 +24,11 @@ const notify_audience_1 = require("./notify-audience");
24
24
  */
25
25
  function addCommentRequest(context, params, options) {
26
26
  const normalized = (0, notify_audience_1.applyNotifyAudience)(params);
27
- const withId = { ...normalized, id: (0, uuidv7_1.resolveCreateId)(normalized.id) };
27
+ const withId = {
28
+ ...normalized,
29
+ id: (0, uuidv7_1.resolveCreateId)(normalized.id),
30
+ threadId: (0, uuidv7_1.resolveReferenceId)(normalized.threadId, 'threadId'),
31
+ };
28
32
  const payload = options?.threadAction
29
33
  ? { ...withId, threadAction: options.threadAction }
30
34
  : withId;
@@ -31,26 +31,55 @@ class ConversationsClient extends base_client_1.BaseClient {
31
31
  : exports.ConversationListSchema;
32
32
  }
33
33
  /**
34
- * Gets all conversations for a workspace.
34
+ * Gets a page of conversations for a workspace, newest activity first.
35
+ * The server returns at most 500 rows per request (20 by default); pass
36
+ * the last row's `lastActive`/`id` as `olderThan`/`beforeId` to fetch
37
+ * the next page. Paired, they form a strict compound boundary, so pages
38
+ * never repeat rows. Omitting `archived` returns active and archived
39
+ * conversations mixed.
35
40
  *
36
41
  * @param args - The arguments for getting conversations.
37
42
  * @param args.workspaceId - The workspace ID.
38
- * @param args.archived - Optional flag to include archived conversations.
43
+ * @param args.archived - Optional flag to filter archived (true) or active (false) conversations.
44
+ * @param args.olderThan - Optional date to get conversations last active before.
45
+ * @param args.beforeId - Optional conversation id. Paired with olderThan it forms the
46
+ * strict compound cursor; alone it pages by conversation id order instead.
47
+ * @param args.limit - Optional page size (server default 20, max 500).
39
48
  * @returns An array of conversation objects.
40
49
  *
41
50
  * @example
42
51
  * ```typescript
43
- * const conversations = await api.conversations.getConversations({ workspaceId: 123 })
44
- * conversations.forEach(c => console.log(c.title))
52
+ * const page = await api.conversations.getConversations({ workspaceId: 123, limit: 500 })
53
+ * const last = page.at(-1)
54
+ * const nextPage = last
55
+ * ? await api.conversations.getConversations({
56
+ * workspaceId: 123,
57
+ * limit: 500,
58
+ * olderThan: last.lastActive,
59
+ * beforeId: last.id,
60
+ * })
61
+ * : []
45
62
  * ```
46
63
  */
47
64
  getConversations(args) {
65
+ // Fields are picked explicitly (matching getThreads/getComments) so a
66
+ // future Date field can't silently reach the generic snake-casing,
67
+ // which would turn it into an empty object on the wire.
68
+ const params = { workspaceId: args.workspaceId };
69
+ if (args.archived != null)
70
+ params.archived = args.archived;
71
+ if (args.olderThan)
72
+ params.olderThanTs = Math.floor(args.olderThan.getTime() / 1000);
73
+ if (args.beforeId != null)
74
+ params.beforeId = args.beforeId;
75
+ if (args.limit != null)
76
+ params.limit = args.limit;
48
77
  return (0, http_client_1.request)({
49
78
  httpMethod: 'GET',
50
79
  baseUri: this.getBaseUri(),
51
80
  relativePath: `${endpoints_1.ENDPOINT_CONVERSATIONS}/get`,
52
81
  apiToken: this.apiToken,
53
- payload: args,
82
+ payload: params,
54
83
  customFetch: this.customFetch,
55
84
  }).then((response) => this.conversationListSchema.parse(response.data));
56
85
  }
@@ -116,7 +116,11 @@ class ThreadsClient extends base_client_1.BaseClient {
116
116
  */
117
117
  createThread(args) {
118
118
  const normalized = (0, notify_audience_1.applyNotifyAudience)(args);
119
- return this.simple('POST', 'add', { ...normalized, id: (0, uuidv7_1.resolveCreateId)(args.id) }, this.threadSchema);
119
+ return this.simple('POST', 'add', {
120
+ ...normalized,
121
+ id: (0, uuidv7_1.resolveCreateId)(normalized.id),
122
+ channelId: (0, uuidv7_1.resolveReferenceId)(normalized.channelId, 'channelId'),
123
+ }, this.threadSchema);
120
124
  }
121
125
  /**
122
126
  * Partial update of an existing thread.
@@ -344,7 +348,7 @@ class ThreadsClient extends base_client_1.BaseClient {
344
348
  apiToken: this.apiToken,
345
349
  customFetch: this.customFetch,
346
350
  schema: this.commentSchema,
347
- }, { threadId: id, ...rest }, { threadAction });
351
+ }, { threadId: (0, uuidv7_1.resolveReferenceId)(id, 'id'), ...rest }, { threadAction });
348
352
  }
349
353
  simple(httpMethod, suffix, params, schema) {
350
354
  return (0, http_client_1.request)({
@@ -4,9 +4,11 @@ exports.mockWorkspaceUser = exports.mockComment = exports.mockConversation = exp
4
4
  const endpoints_1 = require("../consts/endpoints");
5
5
  exports.TEST_API_TOKEN = 'test-api-token';
6
6
  exports.TEST_API_BASE_URL = (0, endpoints_1.getCommsBaseUri)().replace(/\/$/, '');
7
- // Canonical test IDs — shaped so they pass SDK-side validation.
8
- exports.TEST_CHANNEL_ID = '7YpL3oZ4kZ9vP7Q1tR2sX3y';
9
- exports.TEST_THREAD_ID = '7YpL3oZ4kZ9vP7Q1tR2sX3z';
7
+ // Stable test IDs. TEST_CHANNEL_ID and TEST_THREAD_ID are base58
8
+ // UUIDv7-shaped because create-thread and add-comment paths validate them
9
+ // SDK-side.
10
+ exports.TEST_CHANNEL_ID = 'CQ7yksHFbYFrSPVTaHVCs';
11
+ exports.TEST_THREAD_ID = 'CQ7yksHFbYFrSPVTaHVCt';
10
12
  exports.TEST_COMMENT_ID = '7YpL3oZ4kZ9vP7Q1tR2sX41';
11
13
  exports.TEST_CONVERSATION_ID = '7YpL3oZ4kZ9vP7Q1tR2sX42';
12
14
  exports.TEST_MESSAGE_ID = '7YpL3oZ4kZ9vP7Q1tR2sX43';
@@ -106,6 +106,9 @@ exports.GetCommentsArgsSchema = zod_1.z.object({
106
106
  exports.GetConversationsArgsSchema = zod_1.z.object({
107
107
  workspaceId: zod_1.z.number(),
108
108
  archived: zod_1.z.boolean().nullable().optional(),
109
+ olderThan: zod_1.z.date().nullable().optional(),
110
+ beforeId: zod_1.z.string().nullable().optional(),
111
+ limit: zod_1.z.number().nullable().optional(),
109
112
  });
110
113
  exports.GetOrCreateConversationArgsSchema = zod_1.z.object({
111
114
  workspaceId: zod_1.z.number(),
@@ -4,6 +4,7 @@ exports.UuidV7Error = void 0;
4
4
  exports.encodeUuidToBase58 = encodeUuidToBase58;
5
5
  exports.decodeBase58ToUuidBytes = decodeBase58ToUuidBytes;
6
6
  exports.generateId = generateId;
7
+ exports.resolveReferenceId = resolveReferenceId;
7
8
  exports.resolveCreateId = resolveCreateId;
8
9
  exports.isValidUuidV7Base58 = isValidUuidV7Base58;
9
10
  exports.base58FromUuidString = base58FromUuidString;
@@ -112,6 +113,13 @@ function generateId() {
112
113
  const hex = (0, uuid_1.v7)().replace(/-/g, '');
113
114
  return encodeUuidToBase58(hexToBytes(hex));
114
115
  }
116
+ /** Validate an existing entity ID without minting a replacement. */
117
+ function resolveReferenceId(id, fieldName) {
118
+ if (!isValidUuidV7Base58(id)) {
119
+ throw new UuidV7Error(`invalid ${fieldName} ${JSON.stringify(id)} — use a base58 UUIDv7 value for \`${fieldName}\`.`);
120
+ }
121
+ return id;
122
+ }
115
123
  /**
116
124
  * Resolve the `id` for a create-style API call: validate the caller-supplied
117
125
  * value (throwing {@link UuidV7Error} before the request leaves the SDK) or
@@ -1,6 +1,6 @@
1
1
  import { ENDPOINT_COMMENTS } from '../consts/endpoints.js';
2
2
  import { request } from '../transport/http-client.js';
3
- import { resolveCreateId } from '../utils/uuidv7.js';
3
+ import { resolveCreateId, resolveReferenceId } from '../utils/uuidv7.js';
4
4
  import { applyNotifyAudience } from './notify-audience.js';
5
5
  /**
6
6
  * Internal helper that powers `comments.createComment`,
@@ -21,7 +21,11 @@ import { applyNotifyAudience } from './notify-audience.js';
21
21
  */
22
22
  export function addCommentRequest(context, params, options) {
23
23
  const normalized = applyNotifyAudience(params);
24
- const withId = { ...normalized, id: resolveCreateId(normalized.id) };
24
+ const withId = {
25
+ ...normalized,
26
+ id: resolveCreateId(normalized.id),
27
+ threadId: resolveReferenceId(normalized.threadId, 'threadId'),
28
+ };
25
29
  const payload = options?.threadAction
26
30
  ? { ...withId, threadAction: options.threadAction }
27
31
  : withId;
@@ -28,26 +28,55 @@ export class ConversationsClient extends BaseClient {
28
28
  : ConversationListSchema;
29
29
  }
30
30
  /**
31
- * Gets all conversations for a workspace.
31
+ * Gets a page of conversations for a workspace, newest activity first.
32
+ * The server returns at most 500 rows per request (20 by default); pass
33
+ * the last row's `lastActive`/`id` as `olderThan`/`beforeId` to fetch
34
+ * the next page. Paired, they form a strict compound boundary, so pages
35
+ * never repeat rows. Omitting `archived` returns active and archived
36
+ * conversations mixed.
32
37
  *
33
38
  * @param args - The arguments for getting conversations.
34
39
  * @param args.workspaceId - The workspace ID.
35
- * @param args.archived - Optional flag to include archived conversations.
40
+ * @param args.archived - Optional flag to filter archived (true) or active (false) conversations.
41
+ * @param args.olderThan - Optional date to get conversations last active before.
42
+ * @param args.beforeId - Optional conversation id. Paired with olderThan it forms the
43
+ * strict compound cursor; alone it pages by conversation id order instead.
44
+ * @param args.limit - Optional page size (server default 20, max 500).
36
45
  * @returns An array of conversation objects.
37
46
  *
38
47
  * @example
39
48
  * ```typescript
40
- * const conversations = await api.conversations.getConversations({ workspaceId: 123 })
41
- * conversations.forEach(c => console.log(c.title))
49
+ * const page = await api.conversations.getConversations({ workspaceId: 123, limit: 500 })
50
+ * const last = page.at(-1)
51
+ * const nextPage = last
52
+ * ? await api.conversations.getConversations({
53
+ * workspaceId: 123,
54
+ * limit: 500,
55
+ * olderThan: last.lastActive,
56
+ * beforeId: last.id,
57
+ * })
58
+ * : []
42
59
  * ```
43
60
  */
44
61
  getConversations(args) {
62
+ // Fields are picked explicitly (matching getThreads/getComments) so a
63
+ // future Date field can't silently reach the generic snake-casing,
64
+ // which would turn it into an empty object on the wire.
65
+ const params = { workspaceId: args.workspaceId };
66
+ if (args.archived != null)
67
+ params.archived = args.archived;
68
+ if (args.olderThan)
69
+ params.olderThanTs = Math.floor(args.olderThan.getTime() / 1000);
70
+ if (args.beforeId != null)
71
+ params.beforeId = args.beforeId;
72
+ if (args.limit != null)
73
+ params.limit = args.limit;
45
74
  return request({
46
75
  httpMethod: 'GET',
47
76
  baseUri: this.getBaseUri(),
48
77
  relativePath: `${ENDPOINT_CONVERSATIONS}/get`,
49
78
  apiToken: this.apiToken,
50
- payload: args,
79
+ payload: params,
51
80
  customFetch: this.customFetch,
52
81
  }).then((response) => this.conversationListSchema.parse(response.data));
53
82
  }
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
  import { ENDPOINT_THREADS } from '../consts/endpoints.js';
3
3
  import { request } from '../transport/http-client.js';
4
4
  import { CommentSchema, createCommentSchema, createThreadSchema, StatusOkSchema, ThreadSchema, UnreadThreadSchema, } from '../types/entities.js';
5
- import { resolveCreateId } from '../utils/uuidv7.js';
5
+ import { resolveCreateId, resolveReferenceId } from '../utils/uuidv7.js';
6
6
  import { addCommentRequest } from './add-comment-helper.js';
7
7
  import { BaseClient } from './base-client.js';
8
8
  import { applyNotifyAudience } from './notify-audience.js';
@@ -113,7 +113,11 @@ export class ThreadsClient extends BaseClient {
113
113
  */
114
114
  createThread(args) {
115
115
  const normalized = applyNotifyAudience(args);
116
- return this.simple('POST', 'add', { ...normalized, id: resolveCreateId(args.id) }, this.threadSchema);
116
+ return this.simple('POST', 'add', {
117
+ ...normalized,
118
+ id: resolveCreateId(normalized.id),
119
+ channelId: resolveReferenceId(normalized.channelId, 'channelId'),
120
+ }, this.threadSchema);
117
121
  }
118
122
  /**
119
123
  * Partial update of an existing thread.
@@ -341,7 +345,7 @@ export class ThreadsClient extends BaseClient {
341
345
  apiToken: this.apiToken,
342
346
  customFetch: this.customFetch,
343
347
  schema: this.commentSchema,
344
- }, { threadId: id, ...rest }, { threadAction });
348
+ }, { threadId: resolveReferenceId(id, 'id'), ...rest }, { threadAction });
345
349
  }
346
350
  simple(httpMethod, suffix, params, schema) {
347
351
  return request({
@@ -1,9 +1,11 @@
1
1
  import { getCommsBaseUri } from '../consts/endpoints.js';
2
2
  export const TEST_API_TOKEN = 'test-api-token';
3
3
  export const TEST_API_BASE_URL = getCommsBaseUri().replace(/\/$/, '');
4
- // Canonical test IDs — shaped so they pass SDK-side validation.
5
- export const TEST_CHANNEL_ID = '7YpL3oZ4kZ9vP7Q1tR2sX3y';
6
- export const TEST_THREAD_ID = '7YpL3oZ4kZ9vP7Q1tR2sX3z';
4
+ // Stable test IDs. TEST_CHANNEL_ID and TEST_THREAD_ID are base58
5
+ // UUIDv7-shaped because create-thread and add-comment paths validate them
6
+ // SDK-side.
7
+ export const TEST_CHANNEL_ID = 'CQ7yksHFbYFrSPVTaHVCs';
8
+ export const TEST_THREAD_ID = 'CQ7yksHFbYFrSPVTaHVCt';
7
9
  export const TEST_COMMENT_ID = '7YpL3oZ4kZ9vP7Q1tR2sX41';
8
10
  export const TEST_CONVERSATION_ID = '7YpL3oZ4kZ9vP7Q1tR2sX42';
9
11
  export const TEST_MESSAGE_ID = '7YpL3oZ4kZ9vP7Q1tR2sX43';
@@ -103,6 +103,9 @@ export const GetCommentsArgsSchema = z.object({
103
103
  export const GetConversationsArgsSchema = z.object({
104
104
  workspaceId: z.number(),
105
105
  archived: z.boolean().nullable().optional(),
106
+ olderThan: z.date().nullable().optional(),
107
+ beforeId: z.string().nullable().optional(),
108
+ limit: z.number().nullable().optional(),
106
109
  });
107
110
  export const GetOrCreateConversationArgsSchema = z.object({
108
111
  workspaceId: z.number(),
@@ -101,6 +101,13 @@ export function generateId() {
101
101
  const hex = uuidv7().replace(/-/g, '');
102
102
  return encodeUuidToBase58(hexToBytes(hex));
103
103
  }
104
+ /** Validate an existing entity ID without minting a replacement. */
105
+ export function resolveReferenceId(id, fieldName) {
106
+ if (!isValidUuidV7Base58(id)) {
107
+ throw new UuidV7Error(`invalid ${fieldName} ${JSON.stringify(id)} — use a base58 UUIDv7 value for \`${fieldName}\`.`);
108
+ }
109
+ return id;
110
+ }
104
111
  /**
105
112
  * Resolve the `id` for a create-style API call: validate the caller-supplied
106
113
  * value (throwing {@link UuidV7Error} before the request leaves the SDK) or
@@ -167,17 +167,34 @@ export declare class ConversationsClient extends BaseClient {
167
167
  private readonly conversationSchema;
168
168
  private readonly conversationListSchema;
169
169
  /**
170
- * Gets all conversations for a workspace.
170
+ * Gets a page of conversations for a workspace, newest activity first.
171
+ * The server returns at most 500 rows per request (20 by default); pass
172
+ * the last row's `lastActive`/`id` as `olderThan`/`beforeId` to fetch
173
+ * the next page. Paired, they form a strict compound boundary, so pages
174
+ * never repeat rows. Omitting `archived` returns active and archived
175
+ * conversations mixed.
171
176
  *
172
177
  * @param args - The arguments for getting conversations.
173
178
  * @param args.workspaceId - The workspace ID.
174
- * @param args.archived - Optional flag to include archived conversations.
179
+ * @param args.archived - Optional flag to filter archived (true) or active (false) conversations.
180
+ * @param args.olderThan - Optional date to get conversations last active before.
181
+ * @param args.beforeId - Optional conversation id. Paired with olderThan it forms the
182
+ * strict compound cursor; alone it pages by conversation id order instead.
183
+ * @param args.limit - Optional page size (server default 20, max 500).
175
184
  * @returns An array of conversation objects.
176
185
  *
177
186
  * @example
178
187
  * ```typescript
179
- * const conversations = await api.conversations.getConversations({ workspaceId: 123 })
180
- * conversations.forEach(c => console.log(c.title))
188
+ * const page = await api.conversations.getConversations({ workspaceId: 123, limit: 500 })
189
+ * const last = page.at(-1)
190
+ * const nextPage = last
191
+ * ? await api.conversations.getConversations({
192
+ * workspaceId: 123,
193
+ * limit: 500,
194
+ * olderThan: last.lastActive,
195
+ * beforeId: last.id,
196
+ * })
197
+ * : []
181
198
  * ```
182
199
  */
183
200
  getConversations(args: GetConversationsArgs): Promise<Conversation[]>;
@@ -1,8 +1,8 @@
1
1
  import type { Channel, Comment, Conversation, Group, Thread, User, Workspace, WorkspaceUser } from '../types/entities.js';
2
2
  export declare const TEST_API_TOKEN = "test-api-token";
3
3
  export declare const TEST_API_BASE_URL: string;
4
- export declare const TEST_CHANNEL_ID = "7YpL3oZ4kZ9vP7Q1tR2sX3y";
5
- export declare const TEST_THREAD_ID = "7YpL3oZ4kZ9vP7Q1tR2sX3z";
4
+ export declare const TEST_CHANNEL_ID = "CQ7yksHFbYFrSPVTaHVCs";
5
+ export declare const TEST_THREAD_ID = "CQ7yksHFbYFrSPVTaHVCt";
6
6
  export declare const TEST_COMMENT_ID = "7YpL3oZ4kZ9vP7Q1tR2sX41";
7
7
  export declare const TEST_CONVERSATION_ID = "7YpL3oZ4kZ9vP7Q1tR2sX42";
8
8
  export declare const TEST_MESSAGE_ID = "7YpL3oZ4kZ9vP7Q1tR2sX43";
@@ -164,6 +164,9 @@ export type GetCommentsArgs = z.infer<typeof GetCommentsArgsSchema>;
164
164
  export declare const GetConversationsArgsSchema: z.ZodObject<{
165
165
  workspaceId: z.ZodNumber;
166
166
  archived: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
167
+ olderThan: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
168
+ beforeId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
169
+ limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
167
170
  }, z.core.$strip>;
168
171
  export type GetConversationsArgs = z.infer<typeof GetConversationsArgsSchema>;
169
172
  export declare const GetOrCreateConversationArgsSchema: z.ZodObject<{
@@ -14,6 +14,8 @@ export declare function decodeBase58ToUuidBytes(value: string): Uint8Array;
14
14
  * group — the backend requires the client to supply the ID on create.
15
15
  */
16
16
  export declare function generateId(): string;
17
+ /** Validate an existing entity ID without minting a replacement. */
18
+ export declare function resolveReferenceId(id: string, fieldName: string): string;
17
19
  /**
18
20
  * Resolve the `id` for a create-style API call: validate the caller-supplied
19
21
  * value (throwing {@link UuidV7Error} before the request leaves the SDK) or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/comms-sdk",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
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/",