@doist/todoist-api-typescript 6.5.0 → 6.5.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.
|
@@ -1,12 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
5
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
36
|
exports.uploadMultipartFile = uploadMultipartFile;
|
|
7
|
-
const form_data_1 = __importDefault(require("form-data"));
|
|
8
|
-
const fs_1 = require("fs");
|
|
9
|
-
const path_1 = require("path");
|
|
10
37
|
const fetch_with_retry_1 = require("./fetch-with-retry");
|
|
11
38
|
/**
|
|
12
39
|
* Helper function to determine content-type from filename extension.
|
|
@@ -37,11 +64,13 @@ function getContentTypeFromFileName(fileName) {
|
|
|
37
64
|
* This is a shared utility for uploading files to Todoist endpoints that require
|
|
38
65
|
* multipart/form-data content type (e.g., file uploads, workspace logo uploads).
|
|
39
66
|
*
|
|
67
|
+
* Supports both browser (Blob/File) and Node.js (Buffer/ReadableStream/path) environments.
|
|
68
|
+
*
|
|
40
69
|
* @param baseUrl - The base API URL (e.g., https://api.todoist.com/api/v1/)
|
|
41
70
|
* @param authToken - The authentication token
|
|
42
71
|
* @param endpoint - The relative endpoint path (e.g., 'uploads', 'workspaces/logo')
|
|
43
|
-
* @param file - The file content (
|
|
44
|
-
* @param fileName - Optional file name (required for Buffer/Stream, optional for paths)
|
|
72
|
+
* @param file - The file content (Blob/File for browser, or Buffer/ReadableStream/path for Node)
|
|
73
|
+
* @param fileName - Optional file name (required for Buffer/Stream, optional for paths and File objects)
|
|
45
74
|
* @param additionalFields - Additional form fields to include (e.g., project_id, workspace_id)
|
|
46
75
|
* @param requestId - Optional request ID for idempotency
|
|
47
76
|
* @returns The response data from the server
|
|
@@ -72,52 +101,74 @@ function getContentTypeFromFileName(fileName) {
|
|
|
72
101
|
*/
|
|
73
102
|
async function uploadMultipartFile(args) {
|
|
74
103
|
const { baseUrl, authToken, endpoint, file, fileName, additionalFields, requestId, customFetch, } = args;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
104
|
+
// Build the full URL
|
|
105
|
+
const url = `${baseUrl}${endpoint}`;
|
|
106
|
+
let body;
|
|
107
|
+
const headers = {
|
|
108
|
+
Authorization: `Bearer ${authToken}`,
|
|
109
|
+
};
|
|
110
|
+
if (requestId) {
|
|
111
|
+
headers['X-Request-Id'] = requestId;
|
|
82
112
|
}
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
if (file instanceof Blob) {
|
|
114
|
+
// Browser path: use native FormData
|
|
115
|
+
const form = new globalThis.FormData();
|
|
116
|
+
const resolvedFileName = fileName || (file instanceof File ? file.name : undefined) || 'upload';
|
|
117
|
+
form.append('file', file, resolvedFileName);
|
|
118
|
+
for (const [key, value] of Object.entries(additionalFields)) {
|
|
119
|
+
if (value !== undefined && value !== null) {
|
|
120
|
+
form.append(key, value.toString());
|
|
121
|
+
}
|
|
87
122
|
}
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
form.append('file', file, {
|
|
91
|
-
filename: fileName,
|
|
92
|
-
contentType: contentType,
|
|
93
|
-
});
|
|
123
|
+
// Don't set Content-Type — let fetch set it with the correct multipart boundary
|
|
124
|
+
body = form;
|
|
94
125
|
}
|
|
95
126
|
else {
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
127
|
+
// Node path: dynamically import Node-only modules
|
|
128
|
+
const [FormDataModule, fsModule, pathModule] = await Promise.all([
|
|
129
|
+
Promise.resolve().then(() => __importStar(require('form-data'))),
|
|
130
|
+
Promise.resolve().then(() => __importStar(require('fs'))),
|
|
131
|
+
Promise.resolve().then(() => __importStar(require('path'))),
|
|
132
|
+
]);
|
|
133
|
+
const FormData = FormDataModule.default;
|
|
134
|
+
const form = new FormData();
|
|
135
|
+
if (typeof file === 'string') {
|
|
136
|
+
// File path - create read stream
|
|
137
|
+
const resolvedFileName = fileName || pathModule.basename(file);
|
|
138
|
+
form.append('file', fsModule.createReadStream(file), resolvedFileName);
|
|
99
139
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
140
|
+
else if (Buffer.isBuffer(file)) {
|
|
141
|
+
// Buffer - require fileName
|
|
142
|
+
if (!fileName) {
|
|
143
|
+
throw new Error('fileName is required when uploading from a Buffer');
|
|
144
|
+
}
|
|
145
|
+
const contentType = getContentTypeFromFileName(fileName);
|
|
146
|
+
form.append('file', file, {
|
|
147
|
+
filename: fileName,
|
|
148
|
+
contentType: contentType,
|
|
149
|
+
});
|
|
106
150
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
151
|
+
else {
|
|
152
|
+
// Stream - require fileName
|
|
153
|
+
if (!fileName) {
|
|
154
|
+
throw new Error('fileName is required when uploading from a stream');
|
|
155
|
+
}
|
|
156
|
+
form.append('file', file, fileName);
|
|
157
|
+
}
|
|
158
|
+
for (const [key, value] of Object.entries(additionalFields)) {
|
|
159
|
+
if (value !== undefined && value !== null) {
|
|
160
|
+
form.append(key, value.toString());
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
Object.assign(headers, form.getHeaders());
|
|
164
|
+
body = form;
|
|
114
165
|
}
|
|
115
166
|
// Make the request using fetch
|
|
116
167
|
const response = await (0, fetch_with_retry_1.fetchWithRetry)({
|
|
117
168
|
url,
|
|
118
169
|
options: {
|
|
119
170
|
method: 'POST',
|
|
120
|
-
body
|
|
171
|
+
body,
|
|
121
172
|
headers,
|
|
122
173
|
timeout: 30000, // 30 second timeout for file uploads
|
|
123
174
|
},
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import FormData from 'form-data';
|
|
2
|
-
import { createReadStream } from 'fs';
|
|
3
|
-
import { basename } from 'path';
|
|
4
1
|
import { fetchWithRetry } from './fetch-with-retry.js';
|
|
5
2
|
/**
|
|
6
3
|
* Helper function to determine content-type from filename extension.
|
|
@@ -31,11 +28,13 @@ function getContentTypeFromFileName(fileName) {
|
|
|
31
28
|
* This is a shared utility for uploading files to Todoist endpoints that require
|
|
32
29
|
* multipart/form-data content type (e.g., file uploads, workspace logo uploads).
|
|
33
30
|
*
|
|
31
|
+
* Supports both browser (Blob/File) and Node.js (Buffer/ReadableStream/path) environments.
|
|
32
|
+
*
|
|
34
33
|
* @param baseUrl - The base API URL (e.g., https://api.todoist.com/api/v1/)
|
|
35
34
|
* @param authToken - The authentication token
|
|
36
35
|
* @param endpoint - The relative endpoint path (e.g., 'uploads', 'workspaces/logo')
|
|
37
|
-
* @param file - The file content (
|
|
38
|
-
* @param fileName - Optional file name (required for Buffer/Stream, optional for paths)
|
|
36
|
+
* @param file - The file content (Blob/File for browser, or Buffer/ReadableStream/path for Node)
|
|
37
|
+
* @param fileName - Optional file name (required for Buffer/Stream, optional for paths and File objects)
|
|
39
38
|
* @param additionalFields - Additional form fields to include (e.g., project_id, workspace_id)
|
|
40
39
|
* @param requestId - Optional request ID for idempotency
|
|
41
40
|
* @returns The response data from the server
|
|
@@ -66,52 +65,74 @@ function getContentTypeFromFileName(fileName) {
|
|
|
66
65
|
*/
|
|
67
66
|
export async function uploadMultipartFile(args) {
|
|
68
67
|
const { baseUrl, authToken, endpoint, file, fileName, additionalFields, requestId, customFetch, } = args;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
// Build the full URL
|
|
69
|
+
const url = `${baseUrl}${endpoint}`;
|
|
70
|
+
let body;
|
|
71
|
+
const headers = {
|
|
72
|
+
Authorization: `Bearer ${authToken}`,
|
|
73
|
+
};
|
|
74
|
+
if (requestId) {
|
|
75
|
+
headers['X-Request-Id'] = requestId;
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
if (file instanceof Blob) {
|
|
78
|
+
// Browser path: use native FormData
|
|
79
|
+
const form = new globalThis.FormData();
|
|
80
|
+
const resolvedFileName = fileName || (file instanceof File ? file.name : undefined) || 'upload';
|
|
81
|
+
form.append('file', file, resolvedFileName);
|
|
82
|
+
for (const [key, value] of Object.entries(additionalFields)) {
|
|
83
|
+
if (value !== undefined && value !== null) {
|
|
84
|
+
form.append(key, value.toString());
|
|
85
|
+
}
|
|
81
86
|
}
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
form.append('file', file, {
|
|
85
|
-
filename: fileName,
|
|
86
|
-
contentType: contentType,
|
|
87
|
-
});
|
|
87
|
+
// Don't set Content-Type — let fetch set it with the correct multipart boundary
|
|
88
|
+
body = form;
|
|
88
89
|
}
|
|
89
90
|
else {
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
// Node path: dynamically import Node-only modules
|
|
92
|
+
const [FormDataModule, fsModule, pathModule] = await Promise.all([
|
|
93
|
+
import('form-data'),
|
|
94
|
+
import('fs'),
|
|
95
|
+
import('path'),
|
|
96
|
+
]);
|
|
97
|
+
const FormData = FormDataModule.default;
|
|
98
|
+
const form = new FormData();
|
|
99
|
+
if (typeof file === 'string') {
|
|
100
|
+
// File path - create read stream
|
|
101
|
+
const resolvedFileName = fileName || pathModule.basename(file);
|
|
102
|
+
form.append('file', fsModule.createReadStream(file), resolvedFileName);
|
|
93
103
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
else if (Buffer.isBuffer(file)) {
|
|
105
|
+
// Buffer - require fileName
|
|
106
|
+
if (!fileName) {
|
|
107
|
+
throw new Error('fileName is required when uploading from a Buffer');
|
|
108
|
+
}
|
|
109
|
+
const contentType = getContentTypeFromFileName(fileName);
|
|
110
|
+
form.append('file', file, {
|
|
111
|
+
filename: fileName,
|
|
112
|
+
contentType: contentType,
|
|
113
|
+
});
|
|
100
114
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
115
|
+
else {
|
|
116
|
+
// Stream - require fileName
|
|
117
|
+
if (!fileName) {
|
|
118
|
+
throw new Error('fileName is required when uploading from a stream');
|
|
119
|
+
}
|
|
120
|
+
form.append('file', file, fileName);
|
|
121
|
+
}
|
|
122
|
+
for (const [key, value] of Object.entries(additionalFields)) {
|
|
123
|
+
if (value !== undefined && value !== null) {
|
|
124
|
+
form.append(key, value.toString());
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
Object.assign(headers, form.getHeaders());
|
|
128
|
+
body = form;
|
|
108
129
|
}
|
|
109
130
|
// Make the request using fetch
|
|
110
131
|
const response = await fetchWithRetry({
|
|
111
132
|
url,
|
|
112
133
|
options: {
|
|
113
134
|
method: 'POST',
|
|
114
|
-
body
|
|
135
|
+
body,
|
|
115
136
|
headers,
|
|
116
137
|
timeout: 30000, // 30 second timeout for file uploads
|
|
117
138
|
},
|
|
@@ -511,10 +511,10 @@ export type UploadFileArgs = {
|
|
|
511
511
|
* - ReadableStream: File content as a stream (requires fileName)
|
|
512
512
|
* - string: Path to a file on the filesystem (fileName is optional, will be inferred from path)
|
|
513
513
|
*/
|
|
514
|
-
file: Buffer | NodeJS.ReadableStream | string;
|
|
514
|
+
file: Buffer | NodeJS.ReadableStream | string | Blob;
|
|
515
515
|
/**
|
|
516
516
|
* The name of the file. Required for Buffer and Stream inputs.
|
|
517
|
-
* Optional for file path strings (will be inferred
|
|
517
|
+
* Optional for file path strings and File objects (will be inferred if not provided).
|
|
518
518
|
*/
|
|
519
519
|
fileName?: string;
|
|
520
520
|
/**
|
|
@@ -609,7 +609,7 @@ export type WorkspaceLogoArgs = {
|
|
|
609
609
|
/**
|
|
610
610
|
* The image file to upload (Buffer, Stream, or file path).
|
|
611
611
|
*/
|
|
612
|
-
file?: Buffer | NodeJS.ReadableStream | string;
|
|
612
|
+
file?: Buffer | NodeJS.ReadableStream | string | Blob;
|
|
613
613
|
/**
|
|
614
614
|
* The file name (required for Buffer/Stream uploads).
|
|
615
615
|
*/
|
|
@@ -3,7 +3,7 @@ type UploadMultipartFileArgs = {
|
|
|
3
3
|
baseUrl: string;
|
|
4
4
|
authToken: string;
|
|
5
5
|
endpoint: string;
|
|
6
|
-
file: Buffer | NodeJS.ReadableStream | string;
|
|
6
|
+
file: Buffer | NodeJS.ReadableStream | string | Blob;
|
|
7
7
|
fileName?: string;
|
|
8
8
|
additionalFields: Record<string, string | number | boolean>;
|
|
9
9
|
requestId?: string;
|
|
@@ -15,11 +15,13 @@ type UploadMultipartFileArgs = {
|
|
|
15
15
|
* This is a shared utility for uploading files to Todoist endpoints that require
|
|
16
16
|
* multipart/form-data content type (e.g., file uploads, workspace logo uploads).
|
|
17
17
|
*
|
|
18
|
+
* Supports both browser (Blob/File) and Node.js (Buffer/ReadableStream/path) environments.
|
|
19
|
+
*
|
|
18
20
|
* @param baseUrl - The base API URL (e.g., https://api.todoist.com/api/v1/)
|
|
19
21
|
* @param authToken - The authentication token
|
|
20
22
|
* @param endpoint - The relative endpoint path (e.g., 'uploads', 'workspaces/logo')
|
|
21
|
-
* @param file - The file content (
|
|
22
|
-
* @param fileName - Optional file name (required for Buffer/Stream, optional for paths)
|
|
23
|
+
* @param file - The file content (Blob/File for browser, or Buffer/ReadableStream/path for Node)
|
|
24
|
+
* @param fileName - Optional file name (required for Buffer/Stream, optional for paths and File objects)
|
|
23
25
|
* @param additionalFields - Additional form fields to include (e.g., project_id, workspace_id)
|
|
24
26
|
* @param requestId - Optional request ID for idempotency
|
|
25
27
|
* @returns The response data from the server
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/todoist-api-typescript",
|
|
3
|
-
"version": "6.5.
|
|
3
|
+
"version": "6.5.1",
|
|
4
4
|
"description": "A typescript wrapper for the Todoist REST API.",
|
|
5
5
|
"author": "Doist developers",
|
|
6
6
|
"repository": "https://github.com/Doist/todoist-api-typescript",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"camelcase": "6.3.0",
|
|
46
46
|
"emoji-regex": "10.6.0",
|
|
47
|
-
"form-data": "4.0.
|
|
47
|
+
"form-data": "4.0.5",
|
|
48
48
|
"ts-custom-error": "^3.2.0",
|
|
49
49
|
"undici": "^7.16.0",
|
|
50
50
|
"uuid": "11.1.0",
|
|
51
|
-
"zod": "4.
|
|
51
|
+
"zod": "4.3.6"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@doist/eslint-config": "12.0.0",
|
|
@@ -63,15 +63,15 @@
|
|
|
63
63
|
"eslint-plugin-prettier": "5.1.3",
|
|
64
64
|
"husky": "9.1.7",
|
|
65
65
|
"jest": "30.1.3",
|
|
66
|
-
"lint-staged": "16.
|
|
67
|
-
"msw": "2.
|
|
66
|
+
"lint-staged": "16.2.7",
|
|
67
|
+
"msw": "2.12.10",
|
|
68
68
|
"npm-run-all2": "8.0.4",
|
|
69
69
|
"obsidian": "^1.10.2-1",
|
|
70
70
|
"prettier": "3.3.2",
|
|
71
|
-
"rimraf": "6.
|
|
72
|
-
"ts-jest": "29.4.
|
|
71
|
+
"rimraf": "6.1.2",
|
|
72
|
+
"ts-jest": "29.4.6",
|
|
73
73
|
"ts-node": "10.9.2",
|
|
74
|
-
"type-fest": "^
|
|
74
|
+
"type-fest": "^5.0.0",
|
|
75
75
|
"typescript": "5.9.3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|