@doist/todoist-sdk 14.2.0 → 15.0.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/esm/types/sync/resources/index.js +1 -0
- package/dist/esm/types/sync/resources/labels.js +7 -0
- package/dist/esm/types/sync/resources/reminders.js +11 -0
- package/dist/esm/types/sync/response.js +3 -4
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/url-helpers.js +151 -23
- package/dist/types/types/sync/resources/index.d.ts +1 -0
- package/dist/types/types/sync/resources/labels.d.ts +12 -0
- package/dist/types/types/sync/resources/reminders.d.ts +87 -0
- package/dist/types/types/sync/response.d.ts +8 -7
- package/dist/types/utils/index.d.ts +2 -1
- package/dist/types/utils/url-helpers.d.ts +88 -0
- package/package.json +5 -10
- package/dist/cjs/authentication.js +0 -317
- package/dist/cjs/clients/activity-client.js +0 -63
- package/dist/cjs/clients/app-client.js +0 -345
- package/dist/cjs/clients/backup-client.js +0 -47
- package/dist/cjs/clients/base-client.js +0 -31
- package/dist/cjs/clients/billing-client.js +0 -152
- package/dist/cjs/clients/comment-client.js +0 -91
- package/dist/cjs/clients/email-client.js +0 -43
- package/dist/cjs/clients/folder-client.js +0 -79
- package/dist/cjs/clients/id-mapping-client.js +0 -39
- package/dist/cjs/clients/insights-client.js +0 -87
- package/dist/cjs/clients/label-client.js +0 -126
- package/dist/cjs/clients/productivity-client.js +0 -27
- package/dist/cjs/clients/project-client.js +0 -235
- package/dist/cjs/clients/reminder-client.js +0 -198
- package/dist/cjs/clients/section-client.js +0 -117
- package/dist/cjs/clients/task-client.js +0 -254
- package/dist/cjs/clients/template-client.js +0 -88
- package/dist/cjs/clients/ui-extension-client.js +0 -107
- package/dist/cjs/clients/upload-client.js +0 -155
- package/dist/cjs/clients/view-options-client.js +0 -25
- package/dist/cjs/clients/workspace-client.js +0 -366
- package/dist/cjs/consts/endpoints.js +0 -224
- package/dist/cjs/index.js +0 -24
- package/dist/cjs/package.json +0 -1
- package/dist/cjs/test-utils/asserts.js +0 -11
- package/dist/cjs/test-utils/msw-setup.js +0 -97
- package/dist/cjs/test-utils/obsidian-fetch-adapter.js +0 -54
- package/dist/cjs/test-utils/test-defaults.js +0 -371
- package/dist/cjs/test-utils/transport-seam.js +0 -17
- package/dist/cjs/test-utils/webhook-fixtures.js +0 -32
- package/dist/cjs/todoist-api.js +0 -1711
- package/dist/cjs/transport/fetch-with-retry.js +0 -219
- package/dist/cjs/transport/http-client.js +0 -134
- package/dist/cjs/transport/http-dispatcher.js +0 -198
- package/dist/cjs/transport/sync-request.js +0 -83
- package/dist/cjs/types/activity/index.js +0 -18
- package/dist/cjs/types/activity/requests.js +0 -2
- package/dist/cjs/types/activity/types.js +0 -28
- package/dist/cjs/types/apps/index.js +0 -18
- package/dist/cjs/types/apps/requests.js +0 -2
- package/dist/cjs/types/apps/types.js +0 -254
- package/dist/cjs/types/backups/index.js +0 -18
- package/dist/cjs/types/backups/requests.js +0 -2
- package/dist/cjs/types/backups/types.js +0 -8
- package/dist/cjs/types/billing/index.js +0 -18
- package/dist/cjs/types/billing/requests.js +0 -2
- package/dist/cjs/types/billing/types.js +0 -92
- package/dist/cjs/types/comments/index.js +0 -18
- package/dist/cjs/types/comments/requests.js +0 -2
- package/dist/cjs/types/comments/types.js +0 -60
- package/dist/cjs/types/common.js +0 -10
- package/dist/cjs/types/emails/index.js +0 -17
- package/dist/cjs/types/emails/requests.js +0 -5
- package/dist/cjs/types/errors.js +0 -29
- package/dist/cjs/types/folders/index.js +0 -17
- package/dist/cjs/types/folders/requests.js +0 -2
- package/dist/cjs/types/http.js +0 -24
- package/dist/cjs/types/id-mappings/index.js +0 -18
- package/dist/cjs/types/id-mappings/requests.js +0 -20
- package/dist/cjs/types/id-mappings/types.js +0 -12
- package/dist/cjs/types/index.js +0 -40
- package/dist/cjs/types/insights/index.js +0 -18
- package/dist/cjs/types/insights/requests.js +0 -2
- package/dist/cjs/types/insights/types.js +0 -80
- package/dist/cjs/types/labels/index.js +0 -18
- package/dist/cjs/types/labels/requests.js +0 -2
- package/dist/cjs/types/labels/types.js +0 -11
- package/dist/cjs/types/productivity/index.js +0 -17
- package/dist/cjs/types/productivity/types.js +0 -68
- package/dist/cjs/types/projects/index.js +0 -18
- package/dist/cjs/types/projects/requests.js +0 -2
- package/dist/cjs/types/projects/types.js +0 -66
- package/dist/cjs/types/reminders/index.js +0 -17
- package/dist/cjs/types/reminders/requests.js +0 -95
- package/dist/cjs/types/sections/index.js +0 -18
- package/dist/cjs/types/sections/requests.js +0 -2
- package/dist/cjs/types/sections/types.js +0 -25
- package/dist/cjs/types/sync/commands/calendars.js +0 -2
- package/dist/cjs/types/sync/commands/filters.js +0 -2
- package/dist/cjs/types/sync/commands/folders.js +0 -2
- package/dist/cjs/types/sync/commands/index.js +0 -34
- package/dist/cjs/types/sync/commands/labels.js +0 -5
- package/dist/cjs/types/sync/commands/notes.js +0 -2
- package/dist/cjs/types/sync/commands/notifications.js +0 -2
- package/dist/cjs/types/sync/commands/others.js +0 -2
- package/dist/cjs/types/sync/commands/project-view-options.js +0 -2
- package/dist/cjs/types/sync/commands/projects.js +0 -2
- package/dist/cjs/types/sync/commands/reminders.js +0 -2
- package/dist/cjs/types/sync/commands/sections.js +0 -2
- package/dist/cjs/types/sync/commands/shared.js +0 -20
- package/dist/cjs/types/sync/commands/sharing.js +0 -2
- package/dist/cjs/types/sync/commands/tasks.js +0 -2
- package/dist/cjs/types/sync/commands/view-options.js +0 -2
- package/dist/cjs/types/sync/commands/workspace-filters.js +0 -2
- package/dist/cjs/types/sync/commands/workspaces.js +0 -2
- package/dist/cjs/types/sync/index.js +0 -22
- package/dist/cjs/types/sync/request.js +0 -2
- package/dist/cjs/types/sync/resource-types.js +0 -39
- package/dist/cjs/types/sync/resources/calendars.js +0 -27
- package/dist/cjs/types/sync/resources/collaborators.js +0 -21
- package/dist/cjs/types/sync/resources/completed-info.js +0 -24
- package/dist/cjs/types/sync/resources/filters.js +0 -14
- package/dist/cjs/types/sync/resources/folders.js +0 -12
- package/dist/cjs/types/sync/resources/index.js +0 -32
- package/dist/cjs/types/sync/resources/live-notifications.js +0 -38
- package/dist/cjs/types/sync/resources/notes.js +0 -21
- package/dist/cjs/types/sync/resources/reminders.js +0 -50
- package/dist/cjs/types/sync/resources/suggestions.js +0 -36
- package/dist/cjs/types/sync/resources/tooltips.js +0 -8
- package/dist/cjs/types/sync/resources/user-plan-limits.js +0 -20
- package/dist/cjs/types/sync/resources/user-settings.js +0 -40
- package/dist/cjs/types/sync/resources/user.js +0 -110
- package/dist/cjs/types/sync/resources/view-options.js +0 -86
- package/dist/cjs/types/sync/resources/workspace-filters.js +0 -19
- package/dist/cjs/types/sync/resources/workspaces.js +0 -72
- package/dist/cjs/types/sync/response.js +0 -54
- package/dist/cjs/types/sync/user-preferences.js +0 -71
- package/dist/cjs/types/tasks/index.js +0 -18
- package/dist/cjs/types/tasks/requests.js +0 -2
- package/dist/cjs/types/tasks/types.js +0 -59
- package/dist/cjs/types/templates/index.js +0 -17
- package/dist/cjs/types/templates/requests.js +0 -2
- package/dist/cjs/types/ui-extensions/index.js +0 -18
- package/dist/cjs/types/ui-extensions/requests.js +0 -2
- package/dist/cjs/types/ui-extensions/types.js +0 -77
- package/dist/cjs/types/uploads/index.js +0 -17
- package/dist/cjs/types/uploads/requests.js +0 -2
- package/dist/cjs/types/users/index.js +0 -17
- package/dist/cjs/types/users/types.js +0 -51
- package/dist/cjs/types/webhooks/comments.js +0 -96
- package/dist/cjs/types/webhooks/envelope.js +0 -26
- package/dist/cjs/types/webhooks/filters.js +0 -30
- package/dist/cjs/types/webhooks/index.js +0 -25
- package/dist/cjs/types/webhooks/labels.js +0 -54
- package/dist/cjs/types/webhooks/projects.js +0 -36
- package/dist/cjs/types/webhooks/reminders.js +0 -35
- package/dist/cjs/types/webhooks/sections.js +0 -53
- package/dist/cjs/types/webhooks/tasks.js +0 -68
- package/dist/cjs/types/webhooks/types.js +0 -55
- package/dist/cjs/types/workspaces/index.js +0 -18
- package/dist/cjs/types/workspaces/requests.js +0 -2
- package/dist/cjs/types/workspaces/types.js +0 -154
- package/dist/cjs/utils/activity-helpers.js +0 -58
- package/dist/cjs/utils/case-conversion.js +0 -69
- package/dist/cjs/utils/colors.js +0 -143
- package/dist/cjs/utils/file-response.js +0 -32
- package/dist/cjs/utils/headers.js +0 -16
- package/dist/cjs/utils/index.js +0 -31
- package/dist/cjs/utils/multipart-upload.js +0 -293
- package/dist/cjs/utils/processing-helpers.js +0 -18
- package/dist/cjs/utils/project-helpers.js +0 -20
- package/dist/cjs/utils/request-helpers.js +0 -21
- package/dist/cjs/utils/sanitization.js +0 -116
- package/dist/cjs/utils/sync-helpers.js +0 -22
- package/dist/cjs/utils/task-sorting.js +0 -281
- package/dist/cjs/utils/uncompletable-helpers.js +0 -60
- package/dist/cjs/utils/url-helpers.js +0 -74
- package/dist/cjs/utils/validators.js +0 -108
- package/dist/cjs/utils/view-options.js +0 -19
- package/dist/cjs/utils/webhook-parser.js +0 -60
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
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
|
-
})();
|
|
35
|
-
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
36
|
-
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
37
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
38
|
-
var m = o[Symbol.asyncIterator], i;
|
|
39
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
40
|
-
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
41
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
42
|
-
};
|
|
43
|
-
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
44
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
45
|
-
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
46
|
-
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
47
|
-
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
48
|
-
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
|
|
49
|
-
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
50
|
-
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
51
|
-
function fulfill(value) { resume("next", value); }
|
|
52
|
-
function reject(value) { resume("throw", value); }
|
|
53
|
-
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
54
|
-
};
|
|
55
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
-
exports.uploadMultipartFile = uploadMultipartFile;
|
|
57
|
-
const uuid_1 = require("uuid");
|
|
58
|
-
const fetch_with_retry_1 = require("../transport/fetch-with-retry");
|
|
59
|
-
const case_conversion_1 = require("./case-conversion");
|
|
60
|
-
/**
|
|
61
|
-
* Helper function to determine content-type from filename extension.
|
|
62
|
-
* @param fileName - The filename to analyze
|
|
63
|
-
* @returns The appropriate MIME type
|
|
64
|
-
*/
|
|
65
|
-
function getContentTypeFromFileName(fileName) {
|
|
66
|
-
const extension = fileName.toLowerCase().split('.').pop();
|
|
67
|
-
switch (extension) {
|
|
68
|
-
case 'png':
|
|
69
|
-
return 'image/png';
|
|
70
|
-
case 'jpg':
|
|
71
|
-
case 'jpeg':
|
|
72
|
-
return 'image/jpeg';
|
|
73
|
-
case 'gif':
|
|
74
|
-
return 'image/gif';
|
|
75
|
-
case 'webp':
|
|
76
|
-
return 'image/webp';
|
|
77
|
-
case 'svg':
|
|
78
|
-
return 'image/svg+xml';
|
|
79
|
-
default:
|
|
80
|
-
return 'application/octet-stream';
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Escapes a value for use inside a quoted `Content-Disposition` parameter,
|
|
85
|
-
* where a raw quote would end the value early and a raw CR or LF would end the
|
|
86
|
-
* header. Note that a lone CR needs escaping too, not just CRLF pairs.
|
|
87
|
-
*
|
|
88
|
-
* This is the same escaping the platform's own `FormData` encoder applies, so
|
|
89
|
-
* servers see exactly what they would from any browser or from undici.
|
|
90
|
-
*/
|
|
91
|
-
function escapeDispositionValue(value) {
|
|
92
|
-
return value.replace(/"/g, '%22').replace(/\r/g, '%0D').replace(/\n/g, '%0A');
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Lays out a `multipart/form-data` payload around a single file part, as the
|
|
96
|
-
* text that goes before and after the file content. Keeping the file content
|
|
97
|
-
* out of it lets the same layout serve both a `Blob` body and a streamed one.
|
|
98
|
-
*/
|
|
99
|
-
function layOutMultipart(args) {
|
|
100
|
-
const { fileName, fileContentType, additionalFields } = args;
|
|
101
|
-
const boundary = `----todoist-sdk-${(0, uuid_1.v4)()}`;
|
|
102
|
-
const head = `--${boundary}\r\n` +
|
|
103
|
-
`Content-Disposition: form-data; name="file"; filename="${escapeDispositionValue(fileName)}"\r\n` +
|
|
104
|
-
`Content-Type: ${fileContentType}\r\n\r\n`;
|
|
105
|
-
let tail = '\r\n';
|
|
106
|
-
for (const [key, value] of Object.entries(additionalFields)) {
|
|
107
|
-
if (value !== undefined && value !== null) {
|
|
108
|
-
tail +=
|
|
109
|
-
`--${boundary}\r\n` +
|
|
110
|
-
`Content-Disposition: form-data; name="${escapeDispositionValue(key)}"\r\n\r\n` +
|
|
111
|
-
`${value.toString()}\r\n`;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
tail += `--${boundary}--\r\n`;
|
|
115
|
-
return { boundary, head, tail };
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Wraps a Node readable stream as a `multipart/form-data` request body.
|
|
119
|
-
*
|
|
120
|
-
* The result is a web `ReadableStream`, which both the runtime's `fetch` and
|
|
121
|
-
* undici's own accept — unlike a Node stream or a `FormData`, either of which
|
|
122
|
-
* only works with one particular client. The file is never buffered, so the
|
|
123
|
-
* request is sent with `Transfer-Encoding: chunked`.
|
|
124
|
-
*/
|
|
125
|
-
function streamMultipartBody(file, parts) {
|
|
126
|
-
const encoder = new TextEncoder();
|
|
127
|
-
function chunks() {
|
|
128
|
-
return __asyncGenerator(this, arguments, function* chunks_1() {
|
|
129
|
-
var _a, e_1, _b, _c;
|
|
130
|
-
yield yield __await(encoder.encode(parts.head));
|
|
131
|
-
try {
|
|
132
|
-
for (var _d = true, file_1 = __asyncValues(file), file_1_1; file_1_1 = yield __await(file_1.next()), _a = file_1_1.done, !_a; _d = true) {
|
|
133
|
-
_c = file_1_1.value;
|
|
134
|
-
_d = false;
|
|
135
|
-
const chunk = _c;
|
|
136
|
-
yield yield __await(typeof chunk === 'string' ? encoder.encode(chunk) : new Uint8Array(chunk));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
140
|
-
finally {
|
|
141
|
-
try {
|
|
142
|
-
if (!_d && !_a && (_b = file_1.return)) yield __await(_b.call(file_1));
|
|
143
|
-
}
|
|
144
|
-
finally { if (e_1) throw e_1.error; }
|
|
145
|
-
}
|
|
146
|
-
yield yield __await(encoder.encode(parts.tail));
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
const iterator = chunks();
|
|
150
|
-
// Driven by hand rather than with `ReadableStream.from`, which is absent
|
|
151
|
-
// from this project's DOM lib types and is still missing in some browsers,
|
|
152
|
-
// or `Readable.toWeb`, which would drag a Node-only import into a module
|
|
153
|
-
// that browsers also load.
|
|
154
|
-
return new ReadableStream({
|
|
155
|
-
async pull(controller) {
|
|
156
|
-
const { value, done } = await iterator.next();
|
|
157
|
-
if (done) {
|
|
158
|
-
controller.close();
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
controller.enqueue(value);
|
|
162
|
-
},
|
|
163
|
-
async cancel(reason) {
|
|
164
|
-
var _a;
|
|
165
|
-
await ((_a = iterator.return) === null || _a === void 0 ? void 0 : _a.call(iterator, reason));
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Builds the request body for a multipart upload.
|
|
171
|
-
*
|
|
172
|
-
* Deliberately avoids `FormData`. A `FormData` body is only encoded by the
|
|
173
|
-
* `fetch` that owns that `FormData` class — undici brands its own — so a body
|
|
174
|
-
* built from the global `FormData` and sent through undici's `fetch`, or the
|
|
175
|
-
* reverse, is stringified to the literal `"[object FormData]"` and the upload
|
|
176
|
-
* silently carries no file. The `form-data` package fares worse: neither
|
|
177
|
-
* `fetch` accepts it as a body at all. `Blob` and `ReadableStream` carry no
|
|
178
|
-
* such brand and work with whichever `fetch` dispatches the request,
|
|
179
|
-
* including a caller-supplied `customFetch`.
|
|
180
|
-
*/
|
|
181
|
-
function buildMultipartBody(args) {
|
|
182
|
-
const { file, fileName, fileContentType, additionalFields } = args;
|
|
183
|
-
const parts = layOutMultipart({ fileName, fileContentType, additionalFields });
|
|
184
|
-
const contentType = `multipart/form-data; boundary=${parts.boundary}`;
|
|
185
|
-
if (file instanceof Blob) {
|
|
186
|
-
// A Blob keeps its length, so the request gets a Content-Length and
|
|
187
|
-
// stays replayable if it has to be retried. File-backed Blobs (from
|
|
188
|
-
// `fs.openAsBlob`) stay lazy here, so nothing is read into memory.
|
|
189
|
-
return { body: new Blob([parts.head, file, parts.tail]), contentType, isStreamed: false };
|
|
190
|
-
}
|
|
191
|
-
return { body: streamMultipartBody(file, parts), contentType, isStreamed: true };
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Uploads a file using multipart/form-data.
|
|
195
|
-
*
|
|
196
|
-
* This is a shared utility for uploading files to Todoist endpoints that require
|
|
197
|
-
* multipart/form-data content type (e.g., file uploads, workspace logo uploads).
|
|
198
|
-
*
|
|
199
|
-
* Supports both browser (Blob/File) and Node.js (Buffer/ReadableStream/path) environments.
|
|
200
|
-
*
|
|
201
|
-
* @param baseUrl - The base API URL (e.g., https://api.todoist.com/api/v1/)
|
|
202
|
-
* @param authToken - The authentication token
|
|
203
|
-
* @param endpoint - The relative endpoint path (e.g., 'uploads', 'workspaces/logo')
|
|
204
|
-
* @param file - The file content (Blob/File for browser, or Buffer/ReadableStream/path for Node)
|
|
205
|
-
* @param fileName - Optional file name (required for Buffer/Stream, optional for paths and File objects)
|
|
206
|
-
* @param additionalFields - Additional form fields to include (e.g., project_id, workspace_id)
|
|
207
|
-
* @param requestId - Optional request ID for idempotency
|
|
208
|
-
* @returns The response data from the server
|
|
209
|
-
*
|
|
210
|
-
* @example
|
|
211
|
-
* ```typescript
|
|
212
|
-
* // Upload from a file path
|
|
213
|
-
* const result = await uploadMultipartFile(
|
|
214
|
-
* 'https://api.todoist.com/api/v1/',
|
|
215
|
-
* 'my-token',
|
|
216
|
-
* 'uploads',
|
|
217
|
-
* '/path/to/file.pdf',
|
|
218
|
-
* undefined,
|
|
219
|
-
* { project_id: '12345' }
|
|
220
|
-
* )
|
|
221
|
-
*
|
|
222
|
-
* // Upload from a Buffer
|
|
223
|
-
* const buffer = Buffer.from('file content')
|
|
224
|
-
* const result = await uploadMultipartFile(
|
|
225
|
-
* 'https://api.todoist.com/api/v1/',
|
|
226
|
-
* 'my-token',
|
|
227
|
-
* 'uploads',
|
|
228
|
-
* buffer,
|
|
229
|
-
* 'document.pdf',
|
|
230
|
-
* { project_id: '12345' }
|
|
231
|
-
* )
|
|
232
|
-
* ```
|
|
233
|
-
*/
|
|
234
|
-
async function uploadMultipartFile(args) {
|
|
235
|
-
const { baseUrl, authToken, endpoint, file, fileName, additionalFields, requestId, customFetch, } = args;
|
|
236
|
-
// Build the full URL
|
|
237
|
-
const url = `${baseUrl}${endpoint}`;
|
|
238
|
-
let body;
|
|
239
|
-
const headers = {
|
|
240
|
-
Authorization: `Bearer ${authToken}`,
|
|
241
|
-
};
|
|
242
|
-
if (requestId) {
|
|
243
|
-
headers['X-Request-Id'] = requestId;
|
|
244
|
-
}
|
|
245
|
-
// Resolve the file to something every `fetch` accepts as a body part: a
|
|
246
|
-
// Blob where the size is known up front, or a Node stream that gets piped
|
|
247
|
-
// through untouched.
|
|
248
|
-
let filePart;
|
|
249
|
-
let resolvedFileName;
|
|
250
|
-
if (file instanceof Blob) {
|
|
251
|
-
filePart = file;
|
|
252
|
-
resolvedFileName = fileName || (file instanceof File ? file.name : undefined) || 'upload';
|
|
253
|
-
}
|
|
254
|
-
else if (typeof file === 'string') {
|
|
255
|
-
// File path: Node-only, so import lazily to keep browser bundles clean.
|
|
256
|
-
const [fsModule, pathModule] = await Promise.all([Promise.resolve().then(() => __importStar(require('fs'))), Promise.resolve().then(() => __importStar(require('path')))]);
|
|
257
|
-
resolvedFileName = fileName || pathModule.basename(file);
|
|
258
|
-
// `openAsBlob` reads the file lazily, so a large upload is not pulled
|
|
259
|
-
// into memory, and the resulting body still has a known length.
|
|
260
|
-
filePart = await fsModule.openAsBlob(file);
|
|
261
|
-
}
|
|
262
|
-
else if (Buffer.isBuffer(file)) {
|
|
263
|
-
if (!fileName) {
|
|
264
|
-
throw new Error('fileName is required when uploading from a Buffer');
|
|
265
|
-
}
|
|
266
|
-
resolvedFileName = fileName;
|
|
267
|
-
filePart = new Blob([
|
|
268
|
-
new Uint8Array(file.buffer, file.byteOffset, file.byteLength),
|
|
269
|
-
]);
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
if (!fileName) {
|
|
273
|
-
throw new Error('fileName is required when uploading from a stream');
|
|
274
|
-
}
|
|
275
|
-
resolvedFileName = fileName;
|
|
276
|
-
filePart = file;
|
|
277
|
-
}
|
|
278
|
-
const multipart = buildMultipartBody({
|
|
279
|
-
file: filePart,
|
|
280
|
-
fileName: resolvedFileName,
|
|
281
|
-
fileContentType: (filePart instanceof Blob ? filePart.type : undefined) ||
|
|
282
|
-
getContentTypeFromFileName(resolvedFileName),
|
|
283
|
-
additionalFields,
|
|
284
|
-
});
|
|
285
|
-
// The boundary is ours, so Content-Type has to be set explicitly — `fetch`
|
|
286
|
-
// only fills it in for a `FormData` body.
|
|
287
|
-
headers['Content-Type'] = multipart.contentType;
|
|
288
|
-
body = multipart.body;
|
|
289
|
-
// Make the request using fetch
|
|
290
|
-
const response = await (0, fetch_with_retry_1.fetchWithRetry)(Object.assign(Object.assign({ url, options: Object.assign({ method: 'POST', body,
|
|
291
|
-
headers, timeout: 30000 }, (multipart.isStreamed ? { duplex: 'half' } : {})) }, (multipart.isStreamed ? { retryConfig: { retries: 0 } } : {})), { customFetch }));
|
|
292
|
-
return (0, case_conversion_1.camelCaseKeys)(response.data);
|
|
293
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.customCamelCase = customCamelCase;
|
|
7
|
-
const camelcase_1 = __importDefault(require("camelcase"));
|
|
8
|
-
const emoji_regex_1 = __importDefault(require("emoji-regex"));
|
|
9
|
-
function isEmojiKey(key) {
|
|
10
|
-
const regex = (0, emoji_regex_1.default)();
|
|
11
|
-
return regex.test(key);
|
|
12
|
-
}
|
|
13
|
-
function customCamelCase(input) {
|
|
14
|
-
// If the value is a solitary emoji string, return the key as-is
|
|
15
|
-
if (isEmojiKey(input))
|
|
16
|
-
return input;
|
|
17
|
-
return (0, camelcase_1.default)(input);
|
|
18
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isWorkspaceProject = isWorkspaceProject;
|
|
4
|
-
exports.isPersonalProject = isPersonalProject;
|
|
5
|
-
/**
|
|
6
|
-
* Type guard to check if a project is a workspace project.
|
|
7
|
-
* @param project The project to check
|
|
8
|
-
* @returns True if the project is a workspace project
|
|
9
|
-
*/
|
|
10
|
-
function isWorkspaceProject(project) {
|
|
11
|
-
return 'workspaceId' in project;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Type guard to check if a project is a personal project.
|
|
15
|
-
* @param project The project to check
|
|
16
|
-
* @returns True if the project is a personal project
|
|
17
|
-
*/
|
|
18
|
-
function isPersonalProject(project) {
|
|
19
|
-
return !isWorkspaceProject(project);
|
|
20
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generatePath = generatePath;
|
|
4
|
-
exports.spreadIfDefined = spreadIfDefined;
|
|
5
|
-
/**
|
|
6
|
-
* Joins path segments using `/` separator.
|
|
7
|
-
* @param segments A list of **valid** path segments.
|
|
8
|
-
* @returns A joined path.
|
|
9
|
-
*/
|
|
10
|
-
function generatePath(...segments) {
|
|
11
|
-
return segments.join('/');
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Returns `fn(value)` spread into an object if `value` is defined, otherwise an empty object.
|
|
15
|
-
*
|
|
16
|
-
* Used to conditionally include a property on a payload object without
|
|
17
|
-
* resorting to mutation or ternary-built object literals.
|
|
18
|
-
*/
|
|
19
|
-
function spreadIfDefined(value, fn) {
|
|
20
|
-
return value !== undefined ? fn(value) : {};
|
|
21
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSanitizedContent = getSanitizedContent;
|
|
4
|
-
exports.getSanitizedTasks = getSanitizedTasks;
|
|
5
|
-
const BOLD_FORMAT = /(^|[\s!?,;>:]+)(?:\*\*|__|!!)(.+?)(\*\*|__|!!)(?=$|[\s!?,;><:]+)/gi;
|
|
6
|
-
const ITALIC_FORMAT = /(^|[\s!?,;>:]+)(?:\*|_|!)(.+?)(\*|_|!)(?=$|[\s!?,;><:]+)/gi;
|
|
7
|
-
const BOLD_ITALIC_FORMAT = /(^|[\s!?,;>:]+)(?:\*\*\*|___|!!!)(.+?)(\*\*\*|___|!!!)(?=$|[\s!?,;><:]+)/gi;
|
|
8
|
-
const CODE_BLOCK_FORMAT = /```([\s\S]*?)```/gi;
|
|
9
|
-
const CODE_INLINE_FORMAT = /`([^`]+)`/gi;
|
|
10
|
-
const TODOIST_LINK = /((?:(?:onenote:)?[\w-]+):\/\/[^\s]+)\s+[[(]([^)]+)[\])]/gi;
|
|
11
|
-
const MARKDOWN_LINK = /\[(.+?)\]\((.+?)\)/gi;
|
|
12
|
-
const GMAIL_LINK = /\[\[gmail=(.+?),\s*(.+?)\]\]/gi;
|
|
13
|
-
const OUTLOOK_LINK = /\[\[outlook=(.+?),\s*(.+?)\]\]/gi;
|
|
14
|
-
const THUNDERBIRD_LINK = /\[\[thunderbird\n(.+)\n(.+)\n\s*\]\]/gi;
|
|
15
|
-
const FAKE_SECTION_PREFIX = '* ';
|
|
16
|
-
const FAKE_SECTION_SUFFIX = ':';
|
|
17
|
-
function removeStyleFormatting(input) {
|
|
18
|
-
if (!input.includes('!') && !input.includes('*') && !input.includes('_')) {
|
|
19
|
-
return input;
|
|
20
|
-
}
|
|
21
|
-
function removeMarkdown(_match, prefix, text) {
|
|
22
|
-
return `${prefix}${text}`;
|
|
23
|
-
}
|
|
24
|
-
input = input.replace(BOLD_ITALIC_FORMAT, removeMarkdown);
|
|
25
|
-
input = input.replace(BOLD_FORMAT, removeMarkdown);
|
|
26
|
-
input = input.replace(ITALIC_FORMAT, removeMarkdown);
|
|
27
|
-
return input;
|
|
28
|
-
}
|
|
29
|
-
function removeCodeFormatting(input) {
|
|
30
|
-
function removeMarkdown(_match, text) {
|
|
31
|
-
return text;
|
|
32
|
-
}
|
|
33
|
-
input = input.replace(CODE_BLOCK_FORMAT, removeMarkdown);
|
|
34
|
-
input = input.replace(CODE_INLINE_FORMAT, removeMarkdown);
|
|
35
|
-
return input;
|
|
36
|
-
}
|
|
37
|
-
function removeFakeSectionFormatting(input) {
|
|
38
|
-
if (input.startsWith(FAKE_SECTION_PREFIX)) {
|
|
39
|
-
input = input.slice(FAKE_SECTION_PREFIX.length);
|
|
40
|
-
}
|
|
41
|
-
if (input.endsWith(FAKE_SECTION_SUFFIX)) {
|
|
42
|
-
input = input.slice(0, input.length - FAKE_SECTION_SUFFIX.length);
|
|
43
|
-
}
|
|
44
|
-
return input;
|
|
45
|
-
}
|
|
46
|
-
function removeMarkdownLinks(input) {
|
|
47
|
-
if (!input.includes('[') || !input.includes(']')) {
|
|
48
|
-
return input;
|
|
49
|
-
}
|
|
50
|
-
function removeMarkdown(_match, text) {
|
|
51
|
-
return text;
|
|
52
|
-
}
|
|
53
|
-
return input.replace(MARKDOWN_LINK, removeMarkdown);
|
|
54
|
-
}
|
|
55
|
-
function removeTodoistLinks(input) {
|
|
56
|
-
if (!input.includes('(') || !input.includes(')')) {
|
|
57
|
-
return input;
|
|
58
|
-
}
|
|
59
|
-
function removeMarkdown(_match, _url, text) {
|
|
60
|
-
return text;
|
|
61
|
-
}
|
|
62
|
-
return input.replace(TODOIST_LINK, removeMarkdown);
|
|
63
|
-
}
|
|
64
|
-
function removeAppLinks(input) {
|
|
65
|
-
if (input.includes('gmail')) {
|
|
66
|
-
input = input.replace(GMAIL_LINK, (_match, _id, text) => text);
|
|
67
|
-
}
|
|
68
|
-
if (input.includes('outlook')) {
|
|
69
|
-
input = input.replace(OUTLOOK_LINK, (_match, _id, text) => text);
|
|
70
|
-
}
|
|
71
|
-
if (input.includes('thunderbird')) {
|
|
72
|
-
input = input.replace(THUNDERBIRD_LINK, (_match, text) => text);
|
|
73
|
-
}
|
|
74
|
-
return input;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Sanitizes a string by removing Todoist's formatting syntax (e.g. bold, italic, code blocks, links).
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* // Removes bold/italic formatting
|
|
81
|
-
* getSanitizedContent('Some **bold** and *italic*') // 'Some bold and italic'
|
|
82
|
-
*
|
|
83
|
-
* // Removes markdown links
|
|
84
|
-
* getSanitizedContent('A [markdown](http://url.com) link') // 'A markdown link'
|
|
85
|
-
*
|
|
86
|
-
* // Removes app-specific links
|
|
87
|
-
* getSanitizedContent('A [[gmail=id, link from gmail]]') // 'A link from gmail'
|
|
88
|
-
*
|
|
89
|
-
* @param input - The string to sanitize
|
|
90
|
-
* @returns The sanitized string with all formatting removed
|
|
91
|
-
*/
|
|
92
|
-
function getSanitizedContent(input) {
|
|
93
|
-
input = removeStyleFormatting(input);
|
|
94
|
-
input = removeCodeFormatting(input);
|
|
95
|
-
input = removeFakeSectionFormatting(input);
|
|
96
|
-
input = removeMarkdownLinks(input);
|
|
97
|
-
input = removeTodoistLinks(input);
|
|
98
|
-
input = removeAppLinks(input);
|
|
99
|
-
return input;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Takes an array of tasks and returns a new array with sanitized content
|
|
103
|
-
* added as 'sanitizedContent' property to each task.
|
|
104
|
-
*
|
|
105
|
-
* @see {@link getSanitizedContent}
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* const tasks = [{ content: '**Bold** task', ... }]
|
|
109
|
-
* getSanitizedTasks(tasks) // [{ content: '**Bold** task', sanitizedContent: 'Bold task', ... }]
|
|
110
|
-
*
|
|
111
|
-
* @param tasks - Array of Task objects to sanitize
|
|
112
|
-
* @returns Array of tasks with added sanitizedContent property
|
|
113
|
-
*/
|
|
114
|
-
function getSanitizedTasks(tasks) {
|
|
115
|
-
return tasks.map((task) => (Object.assign(Object.assign({}, task), { sanitizedContent: getSanitizedContent(task.content) })));
|
|
116
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createCommand = createCommand;
|
|
4
|
-
const uuid_1 = require("uuid");
|
|
5
|
-
/**
|
|
6
|
-
* Creates a strongly-typed Sync API command with an auto-generated UUID.
|
|
7
|
-
*
|
|
8
|
-
* @param type - The command type (e.g. `'item_add'`, `'project_update'`).
|
|
9
|
-
* @param args - The command arguments, constrained by the command type.
|
|
10
|
-
* @param tempId - Optional temporary ID for referencing the created resource in subsequent commands.
|
|
11
|
-
* @returns A fully formed `SyncCommand` ready to include in a sync request.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* const cmd = createCommand('item_add', { content: 'Buy milk', priority: 2 })
|
|
16
|
-
* // cmd.type === 'item_add'
|
|
17
|
-
* // cmd.args is typed as TaskAddArgs
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
function createCommand(type, args, tempId) {
|
|
21
|
-
return Object.assign({ type, uuid: (0, uuid_1.v4)(), args: args }, (tempId !== undefined ? { tempId } : {}));
|
|
22
|
-
}
|