@doist/twist-sdk 2.7.0 → 2.9.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.
Files changed (50) hide show
  1. package/dist/cjs/clients/add-comment-helper.js +1 -2
  2. package/dist/cjs/clients/attachments-client.js +124 -0
  3. package/dist/cjs/clients/base-client.js +10 -0
  4. package/dist/cjs/clients/channels-client.js +9 -6
  5. package/dist/cjs/clients/comments-client.js +16 -6
  6. package/dist/cjs/clients/conversation-messages-client.js +9 -8
  7. package/dist/cjs/clients/conversations-client.js +11 -8
  8. package/dist/cjs/clients/inbox-client.js +6 -3
  9. package/dist/cjs/clients/threads-client.js +18 -9
  10. package/dist/cjs/clients/workspace-users-client.js +9 -2
  11. package/dist/cjs/clients/workspaces-client.js +7 -3
  12. package/dist/cjs/consts/endpoints.js +2 -1
  13. package/dist/cjs/index.js +3 -1
  14. package/dist/cjs/twist-api.js +2 -0
  15. package/dist/cjs/types/entities.js +99 -79
  16. package/dist/cjs/utils/multipart-upload.js +154 -0
  17. package/dist/esm/clients/add-comment-helper.js +1 -2
  18. package/dist/esm/clients/attachments-client.js +121 -0
  19. package/dist/esm/clients/base-client.js +10 -0
  20. package/dist/esm/clients/channels-client.js +10 -7
  21. package/dist/esm/clients/comments-client.js +17 -7
  22. package/dist/esm/clients/conversation-messages-client.js +10 -9
  23. package/dist/esm/clients/conversations-client.js +12 -9
  24. package/dist/esm/clients/inbox-client.js +7 -4
  25. package/dist/esm/clients/threads-client.js +19 -10
  26. package/dist/esm/clients/workspace-users-client.js +9 -2
  27. package/dist/esm/clients/workspaces-client.js +8 -4
  28. package/dist/esm/consts/endpoints.js +1 -0
  29. package/dist/esm/index.js +1 -0
  30. package/dist/esm/twist-api.js +2 -0
  31. package/dist/esm/types/entities.js +92 -78
  32. package/dist/esm/utils/multipart-upload.js +150 -0
  33. package/dist/types/clients/add-comment-helper.d.ts +3 -1
  34. package/dist/types/clients/attachments-client.d.ts +39 -0
  35. package/dist/types/clients/base-client.d.ts +4 -0
  36. package/dist/types/clients/channels-client.d.ts +1 -0
  37. package/dist/types/clients/comments-client.d.ts +1 -0
  38. package/dist/types/clients/conversation-messages-client.d.ts +1 -0
  39. package/dist/types/clients/conversations-client.d.ts +1 -0
  40. package/dist/types/clients/inbox-client.d.ts +1 -0
  41. package/dist/types/clients/threads-client.d.ts +2 -0
  42. package/dist/types/clients/workspace-users-client.d.ts +5 -0
  43. package/dist/types/clients/workspaces-client.d.ts +1 -0
  44. package/dist/types/consts/endpoints.d.ts +1 -0
  45. package/dist/types/index.d.ts +1 -0
  46. package/dist/types/twist-api.d.ts +2 -0
  47. package/dist/types/types/entities.d.ts +1539 -185
  48. package/dist/types/types/requests.d.ts +22 -0
  49. package/dist/types/utils/multipart-upload.d.ts +53 -0
  50. package/package.json +6 -2
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TwistApi = void 0;
4
4
  var batch_builder_1 = require("./batch-builder");
5
+ var attachments_client_1 = require("./clients/attachments-client");
5
6
  var channels_client_1 = require("./clients/channels-client");
6
7
  var comments_client_1 = require("./clients/comments-client");
7
8
  var conversation_messages_client_1 = require("./clients/conversation-messages-client");
@@ -73,6 +74,7 @@ var TwistApi = /** @class */ (function () {
73
74
  this.inbox = new inbox_client_1.InboxClient(clientConfig);
74
75
  this.reactions = new reactions_client_1.ReactionsClient(clientConfig);
75
76
  this.search = new search_client_1.SearchClient(clientConfig);
77
+ this.attachments = new attachments_client_1.AttachmentsClient(clientConfig);
76
78
  }
77
79
  /**
78
80
  * Executes multiple API requests in a single HTTP call using the batch endpoint.
@@ -11,7 +11,13 @@ var __assign = (this && this.__assign) || function () {
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.SearchResultSchema = exports.SEARCH_RESULT_TYPES = exports.UnreadConversationSchema = exports.UnreadThreadSchema = exports.InboxThreadSchema = exports.ConversationMessageSchema = exports.WorkspaceUserSchema = exports.CommentSchema = exports.ConversationSchema = exports.GroupSchema = exports.ThreadSchema = exports.ChannelSchema = exports.WorkspaceSchema = exports.UserSchema = exports.BaseUserSchema = exports.AttachmentSchema = exports.SystemMessageSchema = void 0;
14
+ exports.SearchResultSchema = exports.SEARCH_RESULT_TYPES = exports.UnreadConversationSchema = exports.UnreadThreadSchema = exports.InboxThreadSchema = exports.ConversationMessageSchema = exports.ConversationMessageObjectSchema = exports.WorkspaceUserSchema = exports.CommentSchema = exports.CommentObjectSchema = exports.ConversationSchema = exports.ConversationObjectSchema = exports.GroupSchema = exports.ThreadSchema = exports.ThreadObjectSchema = exports.ChannelSchema = exports.ChannelObjectSchema = exports.WorkspaceSchema = exports.UserSchema = exports.BaseUserSchema = exports.AttachmentSchema = exports.SystemMessageSchema = void 0;
15
+ exports.createChannelSchema = createChannelSchema;
16
+ exports.createThreadSchema = createThreadSchema;
17
+ exports.createConversationSchema = createConversationSchema;
18
+ exports.createCommentSchema = createCommentSchema;
19
+ exports.createConversationMessageSchema = createConversationMessageSchema;
20
+ exports.createInboxThreadSchema = createInboxThreadSchema;
15
21
  var zod_1 = require("zod");
16
22
  var url_helpers_1 = require("../utils/url-helpers");
17
23
  var enums_1 = require("./enums");
@@ -112,8 +118,7 @@ exports.WorkspaceSchema = zod_1.z.object({
112
118
  plan: zod_1.z.enum(enums_1.WORKSPACE_PLANS).nullable().optional(),
113
119
  });
114
120
  // Channel entity from API
115
- exports.ChannelSchema = zod_1.z
116
- .object({
121
+ exports.ChannelObjectSchema = zod_1.z.object({
117
122
  id: zod_1.z.number(),
118
123
  name: zod_1.z.string(),
119
124
  description: zod_1.z.string().nullable().optional(),
@@ -131,11 +136,13 @@ exports.ChannelSchema = zod_1.z
131
136
  icon: zod_1.z.number().nullable().optional(),
132
137
  version: zod_1.z.number(),
133
138
  filters: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).nullable().optional(),
134
- })
135
- .transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({ workspaceId: data.workspaceId, channelId: data.id }) })); });
139
+ });
140
+ function createChannelSchema(linkBaseUrl) {
141
+ return exports.ChannelObjectSchema.transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({ workspaceId: data.workspaceId, channelId: data.id }, linkBaseUrl) })); });
142
+ }
143
+ exports.ChannelSchema = createChannelSchema();
136
144
  // Thread entity from API
137
- exports.ThreadSchema = zod_1.z
138
- .object({
145
+ exports.ThreadObjectSchema = zod_1.z.object({
139
146
  id: zod_1.z.number(),
140
147
  title: zod_1.z.string(),
141
148
  content: zod_1.z.string(),
@@ -198,12 +205,15 @@ exports.ThreadSchema = zod_1.z
198
205
  })
199
206
  .nullable()
200
207
  .optional(),
201
- })
202
- .transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({
203
- workspaceId: data.workspaceId,
204
- channelId: data.channelId,
205
- threadId: data.id,
206
- }) })); });
208
+ });
209
+ function createThreadSchema(linkBaseUrl) {
210
+ return exports.ThreadObjectSchema.transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({
211
+ workspaceId: data.workspaceId,
212
+ channelId: data.channelId,
213
+ threadId: data.id,
214
+ }, linkBaseUrl) })); });
215
+ }
216
+ exports.ThreadSchema = createThreadSchema();
207
217
  // Group entity from API
208
218
  exports.GroupSchema = zod_1.z.object({
209
219
  id: zod_1.z.number(),
@@ -214,8 +224,7 @@ exports.GroupSchema = zod_1.z.object({
214
224
  version: zod_1.z.number(),
215
225
  });
216
226
  // Conversation entity from API
217
- exports.ConversationSchema = zod_1.z
218
- .object({
227
+ exports.ConversationObjectSchema = zod_1.z.object({
219
228
  id: zod_1.z.number(),
220
229
  workspaceId: zod_1.z.number(),
221
230
  userIds: zod_1.z.array(zod_1.z.number()),
@@ -251,11 +260,13 @@ exports.ConversationSchema = zod_1.z
251
260
  })
252
261
  .nullable()
253
262
  .optional(),
254
- })
255
- .transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({ workspaceId: data.workspaceId, conversationId: data.id }) })); });
263
+ });
264
+ function createConversationSchema(linkBaseUrl) {
265
+ return exports.ConversationObjectSchema.transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({ workspaceId: data.workspaceId, conversationId: data.id }, linkBaseUrl) })); });
266
+ }
267
+ exports.ConversationSchema = createConversationSchema();
256
268
  // Comment entity from API
257
- exports.CommentSchema = zod_1.z
258
- .object({
269
+ exports.CommentObjectSchema = zod_1.z.object({
259
270
  id: zod_1.z.number(),
260
271
  content: zod_1.z.string(),
261
272
  creator: zod_1.z.number(),
@@ -280,13 +291,16 @@ exports.CommentSchema = zod_1.z
280
291
  deletedBy: zod_1.z.number().nullable().optional(),
281
292
  version: zod_1.z.number().nullable().optional(),
282
293
  actions: zod_1.z.array(zod_1.z.unknown()).nullable().optional(),
283
- })
284
- .transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({
285
- workspaceId: data.workspaceId,
286
- channelId: data.channelId,
287
- threadId: data.threadId,
288
- commentId: data.id,
289
- }) })); });
294
+ });
295
+ function createCommentSchema(linkBaseUrl) {
296
+ return exports.CommentObjectSchema.transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({
297
+ workspaceId: data.workspaceId,
298
+ channelId: data.channelId,
299
+ threadId: data.threadId,
300
+ commentId: data.id,
301
+ }, linkBaseUrl) })); });
302
+ }
303
+ exports.CommentSchema = createCommentSchema();
290
304
  // WorkspaceUser entity from v4 API
291
305
  exports.WorkspaceUserSchema = exports.BaseUserSchema.extend({
292
306
  email: zod_1.z.string().nullable().optional(),
@@ -298,8 +312,7 @@ exports.WorkspaceUserSchema = exports.BaseUserSchema.extend({
298
312
  version: zod_1.z.number(),
299
313
  });
300
314
  // ConversationMessage entity from API
301
- exports.ConversationMessageSchema = zod_1.z
302
- .object({
315
+ exports.ConversationMessageObjectSchema = zod_1.z.object({
303
316
  id: zod_1.z.number(),
304
317
  content: zod_1.z.string(),
305
318
  creator: zod_1.z.number(),
@@ -316,58 +329,65 @@ exports.ConversationMessageSchema = zod_1.z
316
329
  directMentions: zod_1.z.array(zod_1.z.number()).nullable().optional(),
317
330
  version: zod_1.z.number().nullable().optional(),
318
331
  workspaceId: zod_1.z.number(),
319
- })
320
- .transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({
321
- workspaceId: data.workspaceId,
322
- conversationId: data.conversationId,
323
- messageId: data.id,
324
- }) })); });
332
+ });
333
+ function createConversationMessageSchema(linkBaseUrl) {
334
+ return exports.ConversationMessageObjectSchema.transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({
335
+ workspaceId: data.workspaceId,
336
+ conversationId: data.conversationId,
337
+ messageId: data.id,
338
+ }, linkBaseUrl) })); });
339
+ }
340
+ exports.ConversationMessageSchema = createConversationMessageSchema();
325
341
  // InboxThread entity from API - returns full Thread objects with additional inbox metadata
326
- exports.InboxThreadSchema = zod_1.z
327
- .object({
328
- id: zod_1.z.number(),
329
- title: zod_1.z.string(),
330
- content: zod_1.z.string(),
331
- creator: zod_1.z.number(),
332
- creatorName: zod_1.z.string().nullable().optional(),
333
- channelId: zod_1.z.number(),
334
- workspaceId: zod_1.z.number(),
335
- actions: zod_1.z.array(zod_1.z.unknown()).nullable().optional(),
336
- attachments: zod_1.z.array(exports.AttachmentSchema).nullable().optional(),
337
- commentCount: zod_1.z.number(),
338
- directGroupMentions: zod_1.z.array(zod_1.z.number()).nullable().optional(),
339
- directMentions: zod_1.z.array(zod_1.z.number()).nullable().optional(),
340
- groups: zod_1.z.array(zod_1.z.number()).nullable().optional(),
341
- lastEdited: zod_1.z.date().nullable().optional(),
342
- lastObjIndex: zod_1.z.number().nullable().optional(),
343
- lastUpdated: zod_1.z.date(),
344
- mutedUntil: zod_1.z.date().nullable().optional(),
345
- participants: zod_1.z.array(zod_1.z.number()).nullable().optional(),
346
- pinned: zod_1.z.boolean(),
347
- pinnedDate: zod_1.z.date().nullable().optional(),
348
- posted: zod_1.z.date(),
349
- reactions: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.number())).nullable().optional(),
350
- recipients: zod_1.z.array(zod_1.z.number()).nullable().optional(),
351
- snippet: zod_1.z.string(),
352
- snippetCreator: zod_1.z.number(),
353
- snippetMaskAvatarUrl: zod_1.z.string().nullable().optional(),
354
- snippetMaskPoster: zod_1.z.string().nullable().optional(),
355
- starred: zod_1.z.boolean(),
356
- systemMessage: exports.SystemMessageSchema,
357
- isArchived: zod_1.z.boolean(),
358
- inInbox: zod_1.z.boolean(),
359
- isSaved: zod_1.z.boolean().nullable().optional(),
360
- closed: zod_1.z.boolean(),
361
- responders: zod_1.z.array(zod_1.z.number()).nullable().optional(),
362
- lastComment: exports.CommentSchema.nullable().optional(),
363
- toEmails: zod_1.z.array(zod_1.z.string()).nullable().optional(),
364
- version: zod_1.z.number().nullable().optional(),
365
- })
366
- .transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({
367
- workspaceId: data.workspaceId,
368
- channelId: data.channelId,
369
- threadId: data.id,
370
- }) })); });
342
+ function createInboxThreadObjectSchema(linkBaseUrl) {
343
+ return zod_1.z.object({
344
+ id: zod_1.z.number(),
345
+ title: zod_1.z.string(),
346
+ content: zod_1.z.string(),
347
+ creator: zod_1.z.number(),
348
+ creatorName: zod_1.z.string().nullable().optional(),
349
+ channelId: zod_1.z.number(),
350
+ workspaceId: zod_1.z.number(),
351
+ actions: zod_1.z.array(zod_1.z.unknown()).nullable().optional(),
352
+ attachments: zod_1.z.array(exports.AttachmentSchema).nullable().optional(),
353
+ commentCount: zod_1.z.number(),
354
+ directGroupMentions: zod_1.z.array(zod_1.z.number()).nullable().optional(),
355
+ directMentions: zod_1.z.array(zod_1.z.number()).nullable().optional(),
356
+ groups: zod_1.z.array(zod_1.z.number()).nullable().optional(),
357
+ lastEdited: zod_1.z.date().nullable().optional(),
358
+ lastObjIndex: zod_1.z.number().nullable().optional(),
359
+ lastUpdated: zod_1.z.date(),
360
+ mutedUntil: zod_1.z.date().nullable().optional(),
361
+ participants: zod_1.z.array(zod_1.z.number()).nullable().optional(),
362
+ pinned: zod_1.z.boolean(),
363
+ pinnedDate: zod_1.z.date().nullable().optional(),
364
+ posted: zod_1.z.date(),
365
+ reactions: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.number())).nullable().optional(),
366
+ recipients: zod_1.z.array(zod_1.z.number()).nullable().optional(),
367
+ snippet: zod_1.z.string(),
368
+ snippetCreator: zod_1.z.number(),
369
+ snippetMaskAvatarUrl: zod_1.z.string().nullable().optional(),
370
+ snippetMaskPoster: zod_1.z.string().nullable().optional(),
371
+ starred: zod_1.z.boolean(),
372
+ systemMessage: exports.SystemMessageSchema,
373
+ isArchived: zod_1.z.boolean(),
374
+ inInbox: zod_1.z.boolean(),
375
+ isSaved: zod_1.z.boolean().nullable().optional(),
376
+ closed: zod_1.z.boolean(),
377
+ responders: zod_1.z.array(zod_1.z.number()).nullable().optional(),
378
+ lastComment: createCommentSchema(linkBaseUrl).nullable().optional(),
379
+ toEmails: zod_1.z.array(zod_1.z.string()).nullable().optional(),
380
+ version: zod_1.z.number().nullable().optional(),
381
+ });
382
+ }
383
+ function createInboxThreadSchema(linkBaseUrl) {
384
+ return createInboxThreadObjectSchema(linkBaseUrl).transform(function (data) { return (__assign(__assign({}, data), { url: (0, url_helpers_1.getFullTwistURL)({
385
+ workspaceId: data.workspaceId,
386
+ channelId: data.channelId,
387
+ threadId: data.id,
388
+ }, linkBaseUrl) })); });
389
+ }
390
+ exports.InboxThreadSchema = createInboxThreadSchema();
371
391
  // UnreadThread entity from API - simplified thread reference for unread threads
372
392
  exports.UnreadThreadSchema = zod_1.z.object({
373
393
  threadId: zod_1.z.number(),
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.getContentTypeFromFileName = getContentTypeFromFileName;
51
+ exports.uploadMultipartFile = uploadMultipartFile;
52
+ var fetch_with_retry_1 = require("../transport/fetch-with-retry");
53
+ /**
54
+ * Determine a content-type from a filename extension. Falls back to
55
+ * `application/octet-stream` for unknown extensions.
56
+ */
57
+ function getContentTypeFromFileName(fileName) {
58
+ var extension = fileName.toLowerCase().split('.').pop();
59
+ switch (extension) {
60
+ case 'png':
61
+ return 'image/png';
62
+ case 'jpg':
63
+ case 'jpeg':
64
+ return 'image/jpeg';
65
+ case 'gif':
66
+ return 'image/gif';
67
+ case 'webp':
68
+ return 'image/webp';
69
+ case 'svg':
70
+ return 'image/svg+xml';
71
+ case 'pdf':
72
+ return 'application/pdf';
73
+ default:
74
+ return 'application/octet-stream';
75
+ }
76
+ }
77
+ /**
78
+ * Normalise a supported {@link UploadFile} into a `Blob` plus a resolved file name and
79
+ * content type, so uploads use the cross-platform global `FormData`/`Blob` body that
80
+ * `undici` and browsers both accept natively.
81
+ */
82
+ function toBlob(file, fileName, contentType) {
83
+ if (file instanceof Blob) {
84
+ // `File` is not a global in Node 18, so guard the check before using it.
85
+ var name_1 = fileName ||
86
+ (typeof File !== 'undefined' && file instanceof File ? file.name : undefined) ||
87
+ 'upload';
88
+ var type = contentType || file.type || getContentTypeFromFileName(name_1);
89
+ // Re-wrap only when stamping a type the Blob doesn't already carry.
90
+ var blob = file.type === type ? file : new Blob([file], { type: type });
91
+ return { blob: blob, fileName: name_1, contentType: type };
92
+ }
93
+ if (file instanceof Uint8Array) {
94
+ if (!fileName) {
95
+ throw new Error('fileName is required when uploading raw bytes');
96
+ }
97
+ var type = contentType || getContentTypeFromFileName(fileName);
98
+ // `Blob` accepts any `ArrayBufferView`; the cast satisfies the stricter lib
99
+ // `BlobPart` type (which pins the backing buffer to `ArrayBuffer`).
100
+ return { blob: new Blob([file], { type: type }), fileName: fileName, contentType: type };
101
+ }
102
+ throw new Error('Unsupported file type for upload: expected a Blob or Uint8Array');
103
+ }
104
+ /**
105
+ * Upload a file using `multipart/form-data`.
106
+ *
107
+ * Builds the request body with the global `FormData`/`Blob` so it works unchanged in the
108
+ * browser and in Node.js (via `undici`). The `file` part is sent alongside `file_name`,
109
+ * `file_size`, and `underlying_type` fields (the canonical Twist upload shape); any
110
+ * `additionalFields` are merged in and override the derived values. Authentication uses
111
+ * `Authorization: Bearer`, matching every other Twist SDK client, and `Content-Type` is
112
+ * intentionally left unset so the runtime adds the correct multipart boundary.
113
+ *
114
+ * The response is JSON-parsed and camel-cased by {@link fetchWithRetry}; callers validate
115
+ * the returned shape with the appropriate schema.
116
+ */
117
+ function uploadMultipartFile(args) {
118
+ return __awaiter(this, void 0, void 0, function () {
119
+ var baseUrl, authToken, endpoint, file, fileName, contentType, additionalFields, requestId, customFetch, _a, blob, resolvedFileName, resolvedType, fields, form, _i, _b, _c, key, value, headers, url, response;
120
+ return __generator(this, function (_d) {
121
+ switch (_d.label) {
122
+ case 0:
123
+ baseUrl = args.baseUrl, authToken = args.authToken, endpoint = args.endpoint, file = args.file, fileName = args.fileName, contentType = args.contentType, additionalFields = args.additionalFields, requestId = args.requestId, customFetch = args.customFetch;
124
+ _a = toBlob(file, fileName, contentType), blob = _a.blob, resolvedFileName = _a.fileName, resolvedType = _a.contentType;
125
+ fields = __assign({ file_name: resolvedFileName, file_size: blob.size, underlying_type: resolvedType }, additionalFields);
126
+ form = new FormData();
127
+ form.append('file', blob, resolvedFileName);
128
+ for (_i = 0, _b = Object.entries(fields); _i < _b.length; _i++) {
129
+ _c = _b[_i], key = _c[0], value = _c[1];
130
+ if (value !== undefined && value !== null) {
131
+ form.append(key, String(value));
132
+ }
133
+ }
134
+ headers = {
135
+ Authorization: "Bearer ".concat(authToken),
136
+ };
137
+ if (requestId) {
138
+ headers['X-Request-Id'] = requestId;
139
+ }
140
+ url = new URL(endpoint, baseUrl).toString();
141
+ return [4 /*yield*/, (0, fetch_with_retry_1.fetchWithRetry)(url, {
142
+ method: 'POST',
143
+ headers: headers,
144
+ body: form,
145
+ // Don't set Content-Type — the runtime adds the multipart boundary.
146
+ timeout: 30000,
147
+ }, 3, customFetch)];
148
+ case 1:
149
+ response = _d.sent();
150
+ return [2 /*return*/, response.data];
151
+ }
152
+ });
153
+ });
154
+ }
@@ -31,7 +31,6 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
31
31
  };
32
32
  import { ENDPOINT_COMMENTS } from '../consts/endpoints.js';
33
33
  import { request } from '../transport/http-client.js';
34
- import { CommentSchema } from '../types/entities.js';
35
34
  import { NOTIFY_AUDIENCE_GROUP_IDS, NOTIFY_AUDIENCES } from '../types/enums.js';
36
35
  var SENTINEL_GROUP_IDS = new Set(Object.values(NOTIFY_AUDIENCE_GROUP_IDS));
37
36
  function isNotifyAudience(value) {
@@ -59,7 +58,7 @@ export function addCommentRequest(context, params, options) {
59
58
  var normalized = applyNotifyAudience(params);
60
59
  var payload = (options === null || options === void 0 ? void 0 : options.threadAction)
61
60
  ? __assign(__assign({}, normalized), { threadAction: options.threadAction }) : normalized;
62
- var schema = CommentSchema;
61
+ var schema = context.schema;
63
62
  if (options === null || options === void 0 ? void 0 : options.batch) {
64
63
  return { method: method, url: url, params: payload, schema: schema };
65
64
  }
@@ -0,0 +1,121 @@
1
+ var __extends = (this && this.__extends) || (function () {
2
+ var extendStatics = function (d, b) {
3
+ extendStatics = Object.setPrototypeOf ||
4
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
+ return extendStatics(d, b);
7
+ };
8
+ return function (d, b) {
9
+ if (typeof b !== "function" && b !== null)
10
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
11
+ extendStatics(d, b);
12
+ function __() { this.constructor = d; }
13
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
14
+ };
15
+ })();
16
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
17
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
18
+ return new (P || (P = Promise))(function (resolve, reject) {
19
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
20
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
21
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
22
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
23
+ });
24
+ };
25
+ var __generator = (this && this.__generator) || function (thisArg, body) {
26
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
27
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
28
+ function verb(n) { return function (v) { return step([n, v]); }; }
29
+ function step(op) {
30
+ if (f) throw new TypeError("Generator is already executing.");
31
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
32
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
33
+ if (y = 0, t) op = [op[0] & 2, t.value];
34
+ switch (op[0]) {
35
+ case 0: case 1: t = op; break;
36
+ case 4: _.label++; return { value: op[1], done: false };
37
+ case 5: _.label++; y = op[1]; op = [0]; continue;
38
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
39
+ default:
40
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
41
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
42
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
43
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
44
+ if (t[2]) _.ops.pop();
45
+ _.trys.pop(); continue;
46
+ }
47
+ op = body.call(thisArg, _);
48
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
49
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
50
+ }
51
+ };
52
+ import { v4 as uuid } from 'uuid';
53
+ import { ENDPOINT_ATTACHMENTS } from '../consts/endpoints.js';
54
+ import { AttachmentSchema } from '../types/entities.js';
55
+ import { uploadMultipartFile } from '../utils/multipart-upload.js';
56
+ import { BaseClient } from './base-client.js';
57
+ /**
58
+ * Client for uploading file attachments to Twist.
59
+ *
60
+ * Attachments are uploaded independently, then referenced by passing the returned
61
+ * {@link Attachment} into the `attachments` array of `comments.createComment`,
62
+ * `conversationMessages.createMessage`, and similar calls.
63
+ */
64
+ var AttachmentsClient = /** @class */ (function (_super) {
65
+ __extends(AttachmentsClient, _super);
66
+ function AttachmentsClient() {
67
+ return _super !== null && _super.apply(this, arguments) || this;
68
+ }
69
+ /**
70
+ * Uploads a file and returns the created {@link Attachment}.
71
+ *
72
+ * Mirrors the canonical multipart upload used by twist-web: `POST /attachments/upload`
73
+ * with the `file` binary plus `file_name`, `file_size`, `attachment_id`, and
74
+ * `underlying_type` form fields.
75
+ *
76
+ * @param args - The file to upload and optional metadata.
77
+ * @returns The created attachment, ready to attach to a comment or message.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * import { readFile } from 'node:fs/promises'
82
+ *
83
+ * const attachment = await api.attachments.upload({
84
+ * file: await readFile('./diagram.png'),
85
+ * fileName: 'diagram.png',
86
+ * })
87
+ *
88
+ * await api.comments.createComment({
89
+ * threadId: 789,
90
+ * content: 'See attached',
91
+ * attachments: [attachment],
92
+ * })
93
+ * ```
94
+ */
95
+ AttachmentsClient.prototype.upload = function (args) {
96
+ return __awaiter(this, void 0, void 0, function () {
97
+ var data;
98
+ return __generator(this, function (_a) {
99
+ switch (_a.label) {
100
+ case 0: return [4 /*yield*/, uploadMultipartFile({
101
+ baseUrl: this.getBaseUri(),
102
+ authToken: this.apiToken,
103
+ endpoint: "".concat(ENDPOINT_ATTACHMENTS, "/upload"),
104
+ file: args.file,
105
+ fileName: args.fileName,
106
+ contentType: args.contentType,
107
+ additionalFields: {
108
+ attachment_id: args.attachmentId || uuid(),
109
+ },
110
+ customFetch: this.customFetch,
111
+ })];
112
+ case 1:
113
+ data = _a.sent();
114
+ return [2 /*return*/, AttachmentSchema.parse(data)];
115
+ }
116
+ });
117
+ });
118
+ };
119
+ return AttachmentsClient;
120
+ }(BaseClient));
121
+ export { AttachmentsClient };
@@ -28,6 +28,16 @@ var BaseClient = /** @class */ (function () {
28
28
  // Use centralized helper function for default Twist API URL
29
29
  return getTwistBaseUri(apiVersion);
30
30
  };
31
+ /**
32
+ * Base URL for entity web links, or `undefined` to use getFullTwistURL's default web app.
33
+ */
34
+ BaseClient.prototype.getLinkBaseUrl = function () {
35
+ if (!this.baseUrl) {
36
+ return undefined;
37
+ }
38
+ // Strip a trailing slash so links don't double up, since entity paths start with '/'.
39
+ return this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
40
+ };
31
41
  return BaseClient;
32
42
  }());
33
43
  export { BaseClient };
@@ -16,7 +16,7 @@ var __extends = (this && this.__extends) || (function () {
16
16
  import { z } from 'zod';
17
17
  import { ENDPOINT_CHANNELS } from '../consts/endpoints.js';
18
18
  import { request } from '../transport/http-client.js';
19
- import { ChannelSchema } from '../types/entities.js';
19
+ import { createChannelSchema } from '../types/entities.js';
20
20
  import { BaseClient } from './base-client.js';
21
21
  /**
22
22
  * Client for interacting with Twist channel endpoints.
@@ -24,14 +24,17 @@ import { BaseClient } from './base-client.js';
24
24
  var ChannelsClient = /** @class */ (function (_super) {
25
25
  __extends(ChannelsClient, _super);
26
26
  function ChannelsClient() {
27
- return _super !== null && _super.apply(this, arguments) || this;
27
+ var _this = _super !== null && _super.apply(this, arguments) || this;
28
+ _this.channelSchema = createChannelSchema(_this.getLinkBaseUrl());
29
+ return _this;
28
30
  }
29
31
  ChannelsClient.prototype.getChannels = function (args, options) {
32
+ var _this = this;
30
33
  var method = 'GET';
31
34
  var url = "".concat(ENDPOINT_CHANNELS, "/get");
32
35
  var params = args;
33
36
  if (options === null || options === void 0 ? void 0 : options.batch) {
34
- return { method: method, url: url, params: params, schema: z.array(ChannelSchema) };
37
+ return { method: method, url: url, params: params, schema: z.array(this.channelSchema) };
35
38
  }
36
39
  return request({
37
40
  httpMethod: method,
@@ -40,13 +43,13 @@ var ChannelsClient = /** @class */ (function (_super) {
40
43
  apiToken: this.apiToken,
41
44
  payload: params,
42
45
  customFetch: this.customFetch,
43
- }).then(function (response) { return response.data.map(function (channel) { return ChannelSchema.parse(channel); }); });
46
+ }).then(function (response) { return response.data.map(function (channel) { return _this.channelSchema.parse(channel); }); });
44
47
  };
45
48
  ChannelsClient.prototype.getChannel = function (id, options) {
46
49
  var method = 'GET';
47
50
  var url = "".concat(ENDPOINT_CHANNELS, "/getone");
48
51
  var params = { id: id };
49
- var schema = ChannelSchema;
52
+ var schema = this.channelSchema;
50
53
  if (options === null || options === void 0 ? void 0 : options.batch) {
51
54
  return { method: method, url: url, params: params, schema: schema };
52
55
  }
@@ -63,7 +66,7 @@ var ChannelsClient = /** @class */ (function (_super) {
63
66
  var method = 'POST';
64
67
  var url = "".concat(ENDPOINT_CHANNELS, "/add");
65
68
  var params = args;
66
- var schema = ChannelSchema;
69
+ var schema = this.channelSchema;
67
70
  if (options === null || options === void 0 ? void 0 : options.batch) {
68
71
  return { method: method, url: url, params: params, schema: schema };
69
72
  }
@@ -80,7 +83,7 @@ var ChannelsClient = /** @class */ (function (_super) {
80
83
  var method = 'POST';
81
84
  var url = "".concat(ENDPOINT_CHANNELS, "/update");
82
85
  var params = args;
83
- var schema = ChannelSchema;
86
+ var schema = this.channelSchema;
84
87
  if (options === null || options === void 0 ? void 0 : options.batch) {
85
88
  return { method: method, url: url, params: params, schema: schema };
86
89
  }