@doist/twist-sdk 2.8.1 → 2.9.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.
- package/dist/cjs/clients/attachments-client.js +124 -0
- package/dist/cjs/clients/threads-client.js +7 -0
- package/dist/cjs/consts/endpoints.js +2 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/twist-api.js +2 -0
- package/dist/cjs/types/entities.js +8 -5
- package/dist/cjs/types/requests.js +3 -2
- package/dist/cjs/utils/multipart-upload.js +154 -0
- package/dist/esm/clients/attachments-client.js +121 -0
- package/dist/esm/clients/threads-client.js +7 -0
- package/dist/esm/consts/endpoints.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/twist-api.js +2 -0
- package/dist/esm/types/entities.js +7 -4
- package/dist/esm/types/requests.js +4 -3
- package/dist/esm/utils/multipart-upload.js +150 -0
- package/dist/types/clients/attachments-client.d.ts +39 -0
- package/dist/types/clients/threads-client.d.ts +1 -0
- package/dist/types/consts/endpoints.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/twist-api.d.ts +2 -0
- package/dist/types/types/entities.d.ts +19 -0
- package/dist/types/types/requests.d.ts +37 -2
- package/dist/types/utils/multipart-upload.d.ts +53 -0
- package/package.json +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
18
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
19
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
20
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
21
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
22
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
23
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
27
|
+
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);
|
|
28
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
29
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
30
|
+
function step(op) {
|
|
31
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
32
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
33
|
+
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;
|
|
34
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
35
|
+
switch (op[0]) {
|
|
36
|
+
case 0: case 1: t = op; break;
|
|
37
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
38
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
39
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
40
|
+
default:
|
|
41
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
42
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
43
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
44
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
45
|
+
if (t[2]) _.ops.pop();
|
|
46
|
+
_.trys.pop(); continue;
|
|
47
|
+
}
|
|
48
|
+
op = body.call(thisArg, _);
|
|
49
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
50
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.AttachmentsClient = void 0;
|
|
55
|
+
var uuid_1 = require("uuid");
|
|
56
|
+
var endpoints_1 = require("../consts/endpoints");
|
|
57
|
+
var entities_1 = require("../types/entities");
|
|
58
|
+
var multipart_upload_1 = require("../utils/multipart-upload");
|
|
59
|
+
var base_client_1 = require("./base-client");
|
|
60
|
+
/**
|
|
61
|
+
* Client for uploading file attachments to Twist.
|
|
62
|
+
*
|
|
63
|
+
* Attachments are uploaded independently, then referenced by passing the returned
|
|
64
|
+
* {@link Attachment} into the `attachments` array of `comments.createComment`,
|
|
65
|
+
* `conversationMessages.createMessage`, and similar calls.
|
|
66
|
+
*/
|
|
67
|
+
var AttachmentsClient = /** @class */ (function (_super) {
|
|
68
|
+
__extends(AttachmentsClient, _super);
|
|
69
|
+
function AttachmentsClient() {
|
|
70
|
+
return _super !== null && _super.apply(this, arguments) || this;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Uploads a file and returns the created {@link Attachment}.
|
|
74
|
+
*
|
|
75
|
+
* Mirrors the canonical multipart upload used by twist-web: `POST /attachments/upload`
|
|
76
|
+
* with the `file` binary plus `file_name`, `file_size`, `attachment_id`, and
|
|
77
|
+
* `underlying_type` form fields.
|
|
78
|
+
*
|
|
79
|
+
* @param args - The file to upload and optional metadata.
|
|
80
|
+
* @returns The created attachment, ready to attach to a comment or message.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* import { readFile } from 'node:fs/promises'
|
|
85
|
+
*
|
|
86
|
+
* const attachment = await api.attachments.upload({
|
|
87
|
+
* file: await readFile('./diagram.png'),
|
|
88
|
+
* fileName: 'diagram.png',
|
|
89
|
+
* })
|
|
90
|
+
*
|
|
91
|
+
* await api.comments.createComment({
|
|
92
|
+
* threadId: 789,
|
|
93
|
+
* content: 'See attached',
|
|
94
|
+
* attachments: [attachment],
|
|
95
|
+
* })
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
AttachmentsClient.prototype.upload = function (args) {
|
|
99
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
100
|
+
var data;
|
|
101
|
+
return __generator(this, function (_a) {
|
|
102
|
+
switch (_a.label) {
|
|
103
|
+
case 0: return [4 /*yield*/, (0, multipart_upload_1.uploadMultipartFile)({
|
|
104
|
+
baseUrl: this.getBaseUri(),
|
|
105
|
+
authToken: this.apiToken,
|
|
106
|
+
endpoint: "".concat(endpoints_1.ENDPOINT_ATTACHMENTS, "/upload"),
|
|
107
|
+
file: args.file,
|
|
108
|
+
fileName: args.fileName,
|
|
109
|
+
contentType: args.contentType,
|
|
110
|
+
additionalFields: {
|
|
111
|
+
attachment_id: args.attachmentId || (0, uuid_1.v4)(),
|
|
112
|
+
},
|
|
113
|
+
customFetch: this.customFetch,
|
|
114
|
+
})];
|
|
115
|
+
case 1:
|
|
116
|
+
data = _a.sent();
|
|
117
|
+
return [2 /*return*/, entities_1.AttachmentSchema.parse(data)];
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
return AttachmentsClient;
|
|
123
|
+
}(base_client_1.BaseClient));
|
|
124
|
+
exports.AttachmentsClient = AttachmentsClient;
|
|
@@ -98,6 +98,13 @@ var ThreadsClient = /** @class */ (function (_super) {
|
|
|
98
98
|
var params = args;
|
|
99
99
|
var schema = this.threadSchema;
|
|
100
100
|
if (options === null || options === void 0 ? void 0 : options.batch) {
|
|
101
|
+
// The batch builder URL-encodes array params with `.join(',')`, which turns an
|
|
102
|
+
// array of attachment objects into `[object Object]`. Until batch serialization
|
|
103
|
+
// handles nested objects, reject attachments in batch mode rather than sending a
|
|
104
|
+
// silently broken payload. Upload + attach outside the batch instead.
|
|
105
|
+
if (args.attachments && args.attachments.length > 0) {
|
|
106
|
+
throw new Error('createThread does not support `attachments` in batch mode; create the thread with attachments outside the batch.');
|
|
107
|
+
}
|
|
101
108
|
return { method: method, url: url, params: params, schema: schema };
|
|
102
109
|
}
|
|
103
110
|
return (0, http_client_1.request)({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ENDPOINT_CONVERSATION_MESSAGES = exports.ENDPOINT_SEARCH = exports.ENDPOINT_REACTIONS = exports.ENDPOINT_INBOX = exports.ENDPOINT_NOTIFICATIONS = exports.ENDPOINT_COMMENTS = exports.ENDPOINT_CONVERSATIONS = exports.ENDPOINT_GROUPS = exports.ENDPOINT_THREADS = exports.ENDPOINT_CHANNELS = exports.ENDPOINT_WORKSPACES = exports.ENDPOINT_USERS = exports.API_BASE_URI = exports.API_VERSION = void 0;
|
|
3
|
+
exports.ENDPOINT_ATTACHMENTS = exports.ENDPOINT_CONVERSATION_MESSAGES = exports.ENDPOINT_SEARCH = exports.ENDPOINT_REACTIONS = exports.ENDPOINT_INBOX = exports.ENDPOINT_NOTIFICATIONS = exports.ENDPOINT_COMMENTS = exports.ENDPOINT_CONVERSATIONS = exports.ENDPOINT_GROUPS = exports.ENDPOINT_THREADS = exports.ENDPOINT_CHANNELS = exports.ENDPOINT_WORKSPACES = exports.ENDPOINT_USERS = exports.API_BASE_URI = exports.API_VERSION = void 0;
|
|
4
4
|
exports.getTwistBaseUri = getTwistBaseUri;
|
|
5
5
|
var api_version_1 = require("../types/api-version");
|
|
6
6
|
var BASE_URI = 'https://api.twist.com';
|
|
@@ -33,3 +33,4 @@ exports.ENDPOINT_INBOX = 'inbox';
|
|
|
33
33
|
exports.ENDPOINT_REACTIONS = 'reactions';
|
|
34
34
|
exports.ENDPOINT_SEARCH = 'search';
|
|
35
35
|
exports.ENDPOINT_CONVERSATION_MESSAGES = 'conversation_messages';
|
|
36
|
+
exports.ENDPOINT_ATTACHMENTS = 'attachments';
|
package/dist/cjs/index.js
CHANGED
|
@@ -14,10 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.TwistApi = exports.SearchClient = exports.ReactionsClient = exports.InboxClient = exports.ConversationMessagesClient = exports.BatchBuilder = void 0;
|
|
17
|
+
exports.TwistApi = exports.SearchClient = exports.ReactionsClient = exports.InboxClient = exports.ConversationMessagesClient = exports.AttachmentsClient = exports.BatchBuilder = void 0;
|
|
18
18
|
__exportStar(require("./authentication"), exports);
|
|
19
19
|
var batch_builder_1 = require("./batch-builder");
|
|
20
20
|
Object.defineProperty(exports, "BatchBuilder", { enumerable: true, get: function () { return batch_builder_1.BatchBuilder; } });
|
|
21
|
+
var attachments_client_1 = require("./clients/attachments-client");
|
|
22
|
+
Object.defineProperty(exports, "AttachmentsClient", { enumerable: true, get: function () { return attachments_client_1.AttachmentsClient; } });
|
|
21
23
|
var conversation_messages_client_1 = require("./clients/conversation-messages-client");
|
|
22
24
|
Object.defineProperty(exports, "ConversationMessagesClient", { enumerable: true, get: function () { return conversation_messages_client_1.ConversationMessagesClient; } });
|
|
23
25
|
var inbox_client_1 = require("./clients/inbox-client");
|
package/dist/cjs/twist-api.js
CHANGED
|
@@ -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,7 @@ 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.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;
|
|
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.RequestAttachmentSchema = exports.AttachmentSchema = exports.SystemMessageSchema = void 0;
|
|
15
15
|
exports.createChannelSchema = createChannelSchema;
|
|
16
16
|
exports.createThreadSchema = createThreadSchema;
|
|
17
17
|
exports.createConversationSchema = createConversationSchema;
|
|
@@ -29,8 +29,7 @@ exports.SystemMessageSchema = zod_1.z.union([zod_1.z.string(), zod_1.z.unknown()
|
|
|
29
29
|
// the attachment kind (file vs image vs link preview vs unfurled GIF).
|
|
30
30
|
// Loose: unknown keys from the backend pass through rather than being stripped,
|
|
31
31
|
// so newly-added or off-spec fields stay accessible to callers.
|
|
32
|
-
|
|
33
|
-
.object({
|
|
32
|
+
var attachmentShape = {
|
|
34
33
|
attachmentId: zod_1.z.string(),
|
|
35
34
|
urlType: zod_1.z.string(),
|
|
36
35
|
title: zod_1.z.string().nullable().optional(),
|
|
@@ -47,8 +46,12 @@ exports.AttachmentSchema = zod_1.z
|
|
|
47
46
|
video: zod_1.z.string().nullable().optional(),
|
|
48
47
|
videoType: zod_1.z.string().nullable().optional(),
|
|
49
48
|
videoAutoPlay: zod_1.z.boolean().nullable().optional(),
|
|
50
|
-
}
|
|
51
|
-
|
|
49
|
+
};
|
|
50
|
+
exports.AttachmentSchema = zod_1.z.object(attachmentShape).loose();
|
|
51
|
+
// Request-side attachment schema. Unlike the loose response `AttachmentSchema`,
|
|
52
|
+
// this strips unknown keys so caller typos / off-spec fields are dropped instead
|
|
53
|
+
// of being forwarded on the wire. Use this for `*Args` request schemas.
|
|
54
|
+
exports.RequestAttachmentSchema = zod_1.z.object(attachmentShape);
|
|
52
55
|
// Base user schema with common fields shared between User and WorkspaceUser
|
|
53
56
|
exports.BaseUserSchema = zod_1.z.object({
|
|
54
57
|
id: zod_1.z.number(),
|
|
@@ -35,6 +35,7 @@ exports.CreateThreadArgsSchema = zod_1.z.object({
|
|
|
35
35
|
recipients: zod_1.z.array(zod_1.z.number()).nullable().optional(),
|
|
36
36
|
groups: zod_1.z.array(zod_1.z.number()).nullable().optional(),
|
|
37
37
|
tempId: zod_1.z.number().nullable().optional(),
|
|
38
|
+
attachments: zod_1.z.array(entities_1.RequestAttachmentSchema).nullable().optional(),
|
|
38
39
|
});
|
|
39
40
|
exports.UpdateThreadArgsSchema = zod_1.z.object({
|
|
40
41
|
id: zod_1.z.number(),
|
|
@@ -45,7 +46,7 @@ exports.CreateCommentArgsSchema = zod_1.z.object({
|
|
|
45
46
|
threadId: zod_1.z.number(),
|
|
46
47
|
content: zod_1.z.string(),
|
|
47
48
|
tempId: zod_1.z.number().nullable().optional(),
|
|
48
|
-
attachments: zod_1.z.array(entities_1.
|
|
49
|
+
attachments: zod_1.z.array(entities_1.RequestAttachmentSchema).nullable().optional(),
|
|
49
50
|
actions: zod_1.z.unknown().nullable().optional(),
|
|
50
51
|
recipients: zod_1.z.array(zod_1.z.number()).nullable().optional(),
|
|
51
52
|
groups: zod_1.z.array(zod_1.z.number()).nullable().optional(),
|
|
@@ -66,7 +67,7 @@ exports.CreateMessageArgsSchema = zod_1.z
|
|
|
66
67
|
conversationId: zod_1.z.number().nullable().optional(),
|
|
67
68
|
threadId: zod_1.z.number().nullable().optional(),
|
|
68
69
|
content: zod_1.z.string(),
|
|
69
|
-
attachments: zod_1.z.array(entities_1.
|
|
70
|
+
attachments: zod_1.z.array(entities_1.RequestAttachmentSchema).nullable().optional(),
|
|
70
71
|
})
|
|
71
72
|
.refine(function (data) {
|
|
72
73
|
return ((data.conversationId && !data.threadId) || (!data.conversationId && data.threadId));
|
|
@@ -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
|
+
}
|
|
@@ -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 };
|
|
@@ -95,6 +95,13 @@ var ThreadsClient = /** @class */ (function (_super) {
|
|
|
95
95
|
var params = args;
|
|
96
96
|
var schema = this.threadSchema;
|
|
97
97
|
if (options === null || options === void 0 ? void 0 : options.batch) {
|
|
98
|
+
// The batch builder URL-encodes array params with `.join(',')`, which turns an
|
|
99
|
+
// array of attachment objects into `[object Object]`. Until batch serialization
|
|
100
|
+
// handles nested objects, reject attachments in batch mode rather than sending a
|
|
101
|
+
// silently broken payload. Upload + attach outside the batch instead.
|
|
102
|
+
if (args.attachments && args.attachments.length > 0) {
|
|
103
|
+
throw new Error('createThread does not support `attachments` in batch mode; create the thread with attachments outside the batch.');
|
|
104
|
+
}
|
|
98
105
|
return { method: method, url: url, params: params, schema: schema };
|
|
99
106
|
}
|
|
100
107
|
return request({
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './authentication.js';
|
|
2
2
|
export { BatchBuilder } from './batch-builder.js';
|
|
3
|
+
export { AttachmentsClient } from './clients/attachments-client.js';
|
|
3
4
|
export { ConversationMessagesClient } from './clients/conversation-messages-client.js';
|
|
4
5
|
export { InboxClient } from './clients/inbox-client.js';
|
|
5
6
|
export { ReactionsClient } from './clients/reactions-client.js';
|
package/dist/esm/twist-api.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BatchBuilder } from './batch-builder.js';
|
|
2
|
+
import { AttachmentsClient } from './clients/attachments-client.js';
|
|
2
3
|
import { ChannelsClient } from './clients/channels-client.js';
|
|
3
4
|
import { CommentsClient } from './clients/comments-client.js';
|
|
4
5
|
import { ConversationMessagesClient } from './clients/conversation-messages-client.js';
|
|
@@ -70,6 +71,7 @@ var TwistApi = /** @class */ (function () {
|
|
|
70
71
|
this.inbox = new InboxClient(clientConfig);
|
|
71
72
|
this.reactions = new ReactionsClient(clientConfig);
|
|
72
73
|
this.search = new SearchClient(clientConfig);
|
|
74
|
+
this.attachments = new AttachmentsClient(clientConfig);
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
75
77
|
* Executes multiple API requests in a single HTTP call using the batch endpoint.
|
|
@@ -20,8 +20,7 @@ export var SystemMessageSchema = z.union([z.string(), z.unknown()]).nullable().o
|
|
|
20
20
|
// the attachment kind (file vs image vs link preview vs unfurled GIF).
|
|
21
21
|
// Loose: unknown keys from the backend pass through rather than being stripped,
|
|
22
22
|
// so newly-added or off-spec fields stay accessible to callers.
|
|
23
|
-
|
|
24
|
-
.object({
|
|
23
|
+
var attachmentShape = {
|
|
25
24
|
attachmentId: z.string(),
|
|
26
25
|
urlType: z.string(),
|
|
27
26
|
title: z.string().nullable().optional(),
|
|
@@ -38,8 +37,12 @@ export var AttachmentSchema = z
|
|
|
38
37
|
video: z.string().nullable().optional(),
|
|
39
38
|
videoType: z.string().nullable().optional(),
|
|
40
39
|
videoAutoPlay: z.boolean().nullable().optional(),
|
|
41
|
-
}
|
|
42
|
-
|
|
40
|
+
};
|
|
41
|
+
export var AttachmentSchema = z.object(attachmentShape).loose();
|
|
42
|
+
// Request-side attachment schema. Unlike the loose response `AttachmentSchema`,
|
|
43
|
+
// this strips unknown keys so caller typos / off-spec fields are dropped instead
|
|
44
|
+
// of being forwarded on the wire. Use this for `*Args` request schemas.
|
|
45
|
+
export var RequestAttachmentSchema = z.object(attachmentShape);
|
|
43
46
|
// Base user schema with common fields shared between User and WorkspaceUser
|
|
44
47
|
export var BaseUserSchema = z.object({
|
|
45
48
|
id: z.number(),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { RequestAttachmentSchema } from './entities.js';
|
|
3
3
|
import { NOTIFY_AUDIENCES } from './enums.js';
|
|
4
4
|
export var CreateChannelArgsSchema = z.object({
|
|
5
5
|
workspaceId: z.number(),
|
|
@@ -32,6 +32,7 @@ export var CreateThreadArgsSchema = z.object({
|
|
|
32
32
|
recipients: z.array(z.number()).nullable().optional(),
|
|
33
33
|
groups: z.array(z.number()).nullable().optional(),
|
|
34
34
|
tempId: z.number().nullable().optional(),
|
|
35
|
+
attachments: z.array(RequestAttachmentSchema).nullable().optional(),
|
|
35
36
|
});
|
|
36
37
|
export var UpdateThreadArgsSchema = z.object({
|
|
37
38
|
id: z.number(),
|
|
@@ -42,7 +43,7 @@ export var CreateCommentArgsSchema = z.object({
|
|
|
42
43
|
threadId: z.number(),
|
|
43
44
|
content: z.string(),
|
|
44
45
|
tempId: z.number().nullable().optional(),
|
|
45
|
-
attachments: z.array(
|
|
46
|
+
attachments: z.array(RequestAttachmentSchema).nullable().optional(),
|
|
46
47
|
actions: z.unknown().nullable().optional(),
|
|
47
48
|
recipients: z.array(z.number()).nullable().optional(),
|
|
48
49
|
groups: z.array(z.number()).nullable().optional(),
|
|
@@ -63,7 +64,7 @@ export var CreateMessageArgsSchema = z
|
|
|
63
64
|
conversationId: z.number().nullable().optional(),
|
|
64
65
|
threadId: z.number().nullable().optional(),
|
|
65
66
|
content: z.string(),
|
|
66
|
-
attachments: z.array(
|
|
67
|
+
attachments: z.array(RequestAttachmentSchema).nullable().optional(),
|
|
67
68
|
})
|
|
68
69
|
.refine(function (data) {
|
|
69
70
|
return ((data.conversationId && !data.threadId) || (!data.conversationId && data.threadId));
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
22
|
+
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);
|
|
23
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
24
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
25
|
+
function step(op) {
|
|
26
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
27
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
28
|
+
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;
|
|
29
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
30
|
+
switch (op[0]) {
|
|
31
|
+
case 0: case 1: t = op; break;
|
|
32
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
33
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
34
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
35
|
+
default:
|
|
36
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
37
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
38
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
39
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
40
|
+
if (t[2]) _.ops.pop();
|
|
41
|
+
_.trys.pop(); continue;
|
|
42
|
+
}
|
|
43
|
+
op = body.call(thisArg, _);
|
|
44
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
45
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
import { fetchWithRetry } from '../transport/fetch-with-retry.js';
|
|
49
|
+
/**
|
|
50
|
+
* Determine a content-type from a filename extension. Falls back to
|
|
51
|
+
* `application/octet-stream` for unknown extensions.
|
|
52
|
+
*/
|
|
53
|
+
export function getContentTypeFromFileName(fileName) {
|
|
54
|
+
var extension = fileName.toLowerCase().split('.').pop();
|
|
55
|
+
switch (extension) {
|
|
56
|
+
case 'png':
|
|
57
|
+
return 'image/png';
|
|
58
|
+
case 'jpg':
|
|
59
|
+
case 'jpeg':
|
|
60
|
+
return 'image/jpeg';
|
|
61
|
+
case 'gif':
|
|
62
|
+
return 'image/gif';
|
|
63
|
+
case 'webp':
|
|
64
|
+
return 'image/webp';
|
|
65
|
+
case 'svg':
|
|
66
|
+
return 'image/svg+xml';
|
|
67
|
+
case 'pdf':
|
|
68
|
+
return 'application/pdf';
|
|
69
|
+
default:
|
|
70
|
+
return 'application/octet-stream';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Normalise a supported {@link UploadFile} into a `Blob` plus a resolved file name and
|
|
75
|
+
* content type, so uploads use the cross-platform global `FormData`/`Blob` body that
|
|
76
|
+
* `undici` and browsers both accept natively.
|
|
77
|
+
*/
|
|
78
|
+
function toBlob(file, fileName, contentType) {
|
|
79
|
+
if (file instanceof Blob) {
|
|
80
|
+
// `File` is not a global in Node 18, so guard the check before using it.
|
|
81
|
+
var name_1 = fileName ||
|
|
82
|
+
(typeof File !== 'undefined' && file instanceof File ? file.name : undefined) ||
|
|
83
|
+
'upload';
|
|
84
|
+
var type = contentType || file.type || getContentTypeFromFileName(name_1);
|
|
85
|
+
// Re-wrap only when stamping a type the Blob doesn't already carry.
|
|
86
|
+
var blob = file.type === type ? file : new Blob([file], { type: type });
|
|
87
|
+
return { blob: blob, fileName: name_1, contentType: type };
|
|
88
|
+
}
|
|
89
|
+
if (file instanceof Uint8Array) {
|
|
90
|
+
if (!fileName) {
|
|
91
|
+
throw new Error('fileName is required when uploading raw bytes');
|
|
92
|
+
}
|
|
93
|
+
var type = contentType || getContentTypeFromFileName(fileName);
|
|
94
|
+
// `Blob` accepts any `ArrayBufferView`; the cast satisfies the stricter lib
|
|
95
|
+
// `BlobPart` type (which pins the backing buffer to `ArrayBuffer`).
|
|
96
|
+
return { blob: new Blob([file], { type: type }), fileName: fileName, contentType: type };
|
|
97
|
+
}
|
|
98
|
+
throw new Error('Unsupported file type for upload: expected a Blob or Uint8Array');
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Upload a file using `multipart/form-data`.
|
|
102
|
+
*
|
|
103
|
+
* Builds the request body with the global `FormData`/`Blob` so it works unchanged in the
|
|
104
|
+
* browser and in Node.js (via `undici`). The `file` part is sent alongside `file_name`,
|
|
105
|
+
* `file_size`, and `underlying_type` fields (the canonical Twist upload shape); any
|
|
106
|
+
* `additionalFields` are merged in and override the derived values. Authentication uses
|
|
107
|
+
* `Authorization: Bearer`, matching every other Twist SDK client, and `Content-Type` is
|
|
108
|
+
* intentionally left unset so the runtime adds the correct multipart boundary.
|
|
109
|
+
*
|
|
110
|
+
* The response is JSON-parsed and camel-cased by {@link fetchWithRetry}; callers validate
|
|
111
|
+
* the returned shape with the appropriate schema.
|
|
112
|
+
*/
|
|
113
|
+
export function uploadMultipartFile(args) {
|
|
114
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
115
|
+
var baseUrl, authToken, endpoint, file, fileName, contentType, additionalFields, requestId, customFetch, _a, blob, resolvedFileName, resolvedType, fields, form, _i, _b, _c, key, value, headers, url, response;
|
|
116
|
+
return __generator(this, function (_d) {
|
|
117
|
+
switch (_d.label) {
|
|
118
|
+
case 0:
|
|
119
|
+
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;
|
|
120
|
+
_a = toBlob(file, fileName, contentType), blob = _a.blob, resolvedFileName = _a.fileName, resolvedType = _a.contentType;
|
|
121
|
+
fields = __assign({ file_name: resolvedFileName, file_size: blob.size, underlying_type: resolvedType }, additionalFields);
|
|
122
|
+
form = new FormData();
|
|
123
|
+
form.append('file', blob, resolvedFileName);
|
|
124
|
+
for (_i = 0, _b = Object.entries(fields); _i < _b.length; _i++) {
|
|
125
|
+
_c = _b[_i], key = _c[0], value = _c[1];
|
|
126
|
+
if (value !== undefined && value !== null) {
|
|
127
|
+
form.append(key, String(value));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
headers = {
|
|
131
|
+
Authorization: "Bearer ".concat(authToken),
|
|
132
|
+
};
|
|
133
|
+
if (requestId) {
|
|
134
|
+
headers['X-Request-Id'] = requestId;
|
|
135
|
+
}
|
|
136
|
+
url = new URL(endpoint, baseUrl).toString();
|
|
137
|
+
return [4 /*yield*/, fetchWithRetry(url, {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers: headers,
|
|
140
|
+
body: form,
|
|
141
|
+
// Don't set Content-Type — the runtime adds the multipart boundary.
|
|
142
|
+
timeout: 30000,
|
|
143
|
+
}, 3, customFetch)];
|
|
144
|
+
case 1:
|
|
145
|
+
response = _d.sent();
|
|
146
|
+
return [2 /*return*/, response.data];
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Attachment } from '../types/entities.js';
|
|
2
|
+
import type { UploadAttachmentArgs } from '../types/requests.js';
|
|
3
|
+
import { BaseClient } from './base-client.js';
|
|
4
|
+
/**
|
|
5
|
+
* Client for uploading file attachments to Twist.
|
|
6
|
+
*
|
|
7
|
+
* Attachments are uploaded independently, then referenced by passing the returned
|
|
8
|
+
* {@link Attachment} into the `attachments` array of `comments.createComment`,
|
|
9
|
+
* `conversationMessages.createMessage`, and similar calls.
|
|
10
|
+
*/
|
|
11
|
+
export declare class AttachmentsClient extends BaseClient {
|
|
12
|
+
/**
|
|
13
|
+
* Uploads a file and returns the created {@link Attachment}.
|
|
14
|
+
*
|
|
15
|
+
* Mirrors the canonical multipart upload used by twist-web: `POST /attachments/upload`
|
|
16
|
+
* with the `file` binary plus `file_name`, `file_size`, `attachment_id`, and
|
|
17
|
+
* `underlying_type` form fields.
|
|
18
|
+
*
|
|
19
|
+
* @param args - The file to upload and optional metadata.
|
|
20
|
+
* @returns The created attachment, ready to attach to a comment or message.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { readFile } from 'node:fs/promises'
|
|
25
|
+
*
|
|
26
|
+
* const attachment = await api.attachments.upload({
|
|
27
|
+
* file: await readFile('./diagram.png'),
|
|
28
|
+
* fileName: 'diagram.png',
|
|
29
|
+
* })
|
|
30
|
+
*
|
|
31
|
+
* await api.comments.createComment({
|
|
32
|
+
* threadId: 789,
|
|
33
|
+
* content: 'See attached',
|
|
34
|
+
* attachments: [attachment],
|
|
35
|
+
* })
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
upload(args: UploadAttachmentArgs): Promise<Attachment>;
|
|
39
|
+
}
|
|
@@ -56,6 +56,7 @@ export declare class ThreadsClient extends BaseClient {
|
|
|
56
56
|
* @param args.title - The thread title.
|
|
57
57
|
* @param args.content - The thread content.
|
|
58
58
|
* @param args.recipients - Optional array of user IDs to notify.
|
|
59
|
+
* @param args.attachments - Optional array of {@link Attachment}s (from `attachments.upload`).
|
|
59
60
|
* @param args.sendAsIntegration - Optional flag to send as integration.
|
|
60
61
|
* @param options - Optional configuration. Set `batch: true` to return a descriptor for batch requests.
|
|
61
62
|
* @returns The created thread object.
|
|
@@ -24,3 +24,4 @@ export declare const ENDPOINT_INBOX = "inbox";
|
|
|
24
24
|
export declare const ENDPOINT_REACTIONS = "reactions";
|
|
25
25
|
export declare const ENDPOINT_SEARCH = "search";
|
|
26
26
|
export declare const ENDPOINT_CONVERSATION_MESSAGES = "conversation_messages";
|
|
27
|
+
export declare const ENDPOINT_ATTACHMENTS = "attachments";
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './authentication.js';
|
|
2
2
|
export { BatchBuilder } from './batch-builder.js';
|
|
3
|
+
export { AttachmentsClient } from './clients/attachments-client.js';
|
|
3
4
|
export { ConversationMessagesClient } from './clients/conversation-messages-client.js';
|
|
4
5
|
export { InboxClient } from './clients/inbox-client.js';
|
|
5
6
|
export { ReactionsClient } from './clients/reactions-client.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AttachmentsClient } from './clients/attachments-client.js';
|
|
1
2
|
import { ChannelsClient } from './clients/channels-client.js';
|
|
2
3
|
import { CommentsClient } from './clients/comments-client.js';
|
|
3
4
|
import { ConversationMessagesClient } from './clients/conversation-messages-client.js';
|
|
@@ -42,6 +43,7 @@ export declare class TwistApi {
|
|
|
42
43
|
inbox: InboxClient;
|
|
43
44
|
reactions: ReactionsClient;
|
|
44
45
|
search: SearchClient;
|
|
46
|
+
attachments: AttachmentsClient;
|
|
45
47
|
private authToken;
|
|
46
48
|
private baseUrl?;
|
|
47
49
|
private customFetch?;
|
|
@@ -19,6 +19,25 @@ export declare const AttachmentSchema: z.ZodObject<{
|
|
|
19
19
|
videoAutoPlay: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
20
20
|
}, z.core.$loose>;
|
|
21
21
|
export type Attachment = z.infer<typeof AttachmentSchema>;
|
|
22
|
+
export declare const RequestAttachmentSchema: z.ZodObject<{
|
|
23
|
+
attachmentId: z.ZodString;
|
|
24
|
+
urlType: z.ZodString;
|
|
25
|
+
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
26
|
+
url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
27
|
+
fileName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
28
|
+
fileSize: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
29
|
+
underlyingType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
31
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
32
|
+
imageWidth: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
33
|
+
imageHeight: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
34
|
+
duration: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
35
|
+
uploadState: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
36
|
+
video: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
37
|
+
videoType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
38
|
+
videoAutoPlay: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
export type RequestAttachment = z.infer<typeof RequestAttachmentSchema>;
|
|
22
41
|
export declare const BaseUserSchema: z.ZodObject<{
|
|
23
42
|
id: z.ZodNumber;
|
|
24
43
|
name: z.ZodString;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import type { UploadFile } from '../utils/multipart-upload.js';
|
|
2
3
|
import { type Attachment } from './entities.js';
|
|
3
4
|
export declare const CreateChannelArgsSchema: z.ZodObject<{
|
|
4
5
|
workspaceId: z.ZodNumber;
|
|
@@ -33,6 +34,24 @@ export declare const CreateThreadArgsSchema: z.ZodObject<{
|
|
|
33
34
|
recipients: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber>>>;
|
|
34
35
|
groups: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber>>>;
|
|
35
36
|
tempId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
37
|
+
attachments: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
38
|
+
attachmentId: z.ZodString;
|
|
39
|
+
urlType: z.ZodString;
|
|
40
|
+
title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
41
|
+
url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
42
|
+
fileName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
43
|
+
fileSize: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
44
|
+
underlyingType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
45
|
+
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
46
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
47
|
+
imageWidth: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
48
|
+
imageHeight: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
49
|
+
duration: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
50
|
+
uploadState: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
51
|
+
video: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
52
|
+
videoType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
53
|
+
videoAutoPlay: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
54
|
+
}, z.core.$strip>>>>;
|
|
36
55
|
}, z.core.$strip>;
|
|
37
56
|
export type CreateThreadArgs = z.infer<typeof CreateThreadArgsSchema>;
|
|
38
57
|
export declare const UpdateThreadArgsSchema: z.ZodObject<{
|
|
@@ -62,7 +81,7 @@ export declare const CreateCommentArgsSchema: z.ZodObject<{
|
|
|
62
81
|
video: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
63
82
|
videoType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
64
83
|
videoAutoPlay: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
65
|
-
}, z.core.$
|
|
84
|
+
}, z.core.$strip>>>>;
|
|
66
85
|
actions: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
|
|
67
86
|
recipients: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber>>>;
|
|
68
87
|
groups: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber>>>;
|
|
@@ -105,7 +124,7 @@ export declare const CreateMessageArgsSchema: z.ZodObject<{
|
|
|
105
124
|
video: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
106
125
|
videoType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
107
126
|
videoAutoPlay: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
108
|
-
}, z.core.$
|
|
127
|
+
}, z.core.$strip>>>>;
|
|
109
128
|
}, z.core.$strip>;
|
|
110
129
|
export type CreateMessageArgs = z.infer<typeof CreateMessageArgsSchema>;
|
|
111
130
|
export declare const GetChannelsArgsSchema: z.ZodObject<{
|
|
@@ -375,4 +394,20 @@ export type GetUserLocalTimeArgs = {
|
|
|
375
394
|
workspaceId: number;
|
|
376
395
|
userId: number;
|
|
377
396
|
};
|
|
397
|
+
export type UploadAttachmentArgs = {
|
|
398
|
+
/**
|
|
399
|
+
* The file to upload. Accepts a `Blob`/`File` (browser) or a `Uint8Array` of raw
|
|
400
|
+
* bytes. A Node `Buffer` is a `Uint8Array`, so `await readFile(path)` works directly.
|
|
401
|
+
*/
|
|
402
|
+
file: UploadFile;
|
|
403
|
+
/**
|
|
404
|
+
* File name. Required when `file` is a `Uint8Array`; inferred from the `File.name`
|
|
405
|
+
* otherwise.
|
|
406
|
+
*/
|
|
407
|
+
fileName?: string;
|
|
408
|
+
/** MIME type. Defaults to the `Blob`'s type or one inferred from the file extension. */
|
|
409
|
+
contentType?: string;
|
|
410
|
+
/** Attachment ID to use. A random UUID is generated when omitted. */
|
|
411
|
+
attachmentId?: string;
|
|
412
|
+
};
|
|
378
413
|
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { CustomFetch } from '../types/http.js';
|
|
2
|
+
/**
|
|
3
|
+
* File content accepted by the upload helpers.
|
|
4
|
+
*
|
|
5
|
+
* - `Blob`/`File` — browser, or any runtime with a global `Blob` (Node 18+).
|
|
6
|
+
* - `Uint8Array` — raw bytes (requires `fileName`). A Node `Buffer` is a `Uint8Array`,
|
|
7
|
+
* so reading a file with `fs` and passing the result works without conversion.
|
|
8
|
+
*
|
|
9
|
+
* Both are universal types, so the published `.d.ts` stays free of Node-only globals
|
|
10
|
+
* (`Buffer`, `NodeJS.*`) and the helper pulls in no Node built-ins — keeping the browser
|
|
11
|
+
* bundle clean.
|
|
12
|
+
*/
|
|
13
|
+
export type UploadFile = Blob | Uint8Array;
|
|
14
|
+
type UploadMultipartFileArgs = {
|
|
15
|
+
/** Base API URI with trailing slash, e.g. `https://api.twist.com/api/v3/`. */
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
/** API token used for `Authorization: Bearer`. */
|
|
18
|
+
authToken: string;
|
|
19
|
+
/** Relative endpoint path, e.g. `attachments/upload`. */
|
|
20
|
+
endpoint: string;
|
|
21
|
+
/** File content to upload. */
|
|
22
|
+
file: UploadFile;
|
|
23
|
+
/** File name. Required for raw `Uint8Array` bytes; inferred from a `File`. */
|
|
24
|
+
fileName?: string;
|
|
25
|
+
/** MIME type. Defaults to the `Blob`'s type or one inferred from the file extension. */
|
|
26
|
+
contentType?: string;
|
|
27
|
+
/** Extra multipart fields to send alongside the file metadata fields. */
|
|
28
|
+
additionalFields?: Record<string, string | number | boolean | undefined | null>;
|
|
29
|
+
/** Optional request ID for tracing. */
|
|
30
|
+
requestId?: string;
|
|
31
|
+
/** Optional custom fetch implementation. */
|
|
32
|
+
customFetch?: CustomFetch;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Determine a content-type from a filename extension. Falls back to
|
|
36
|
+
* `application/octet-stream` for unknown extensions.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getContentTypeFromFileName(fileName: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Upload a file using `multipart/form-data`.
|
|
41
|
+
*
|
|
42
|
+
* Builds the request body with the global `FormData`/`Blob` so it works unchanged in the
|
|
43
|
+
* browser and in Node.js (via `undici`). The `file` part is sent alongside `file_name`,
|
|
44
|
+
* `file_size`, and `underlying_type` fields (the canonical Twist upload shape); any
|
|
45
|
+
* `additionalFields` are merged in and override the derived values. Authentication uses
|
|
46
|
+
* `Authorization: Bearer`, matching every other Twist SDK client, and `Content-Type` is
|
|
47
|
+
* intentionally left unset so the runtime adds the correct multipart boundary.
|
|
48
|
+
*
|
|
49
|
+
* The response is JSON-parsed and camel-cased by {@link fetchWithRetry}; callers validate
|
|
50
|
+
* the returned shape with the appropriate schema.
|
|
51
|
+
*/
|
|
52
|
+
export declare function uploadMultipartFile<T>(args: UploadMultipartFileArgs): Promise<T>;
|
|
53
|
+
export {};
|