@doist/todoist-api-typescript 5.8.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/authentication.js +158 -0
- package/dist/cjs/consts/endpoints.js +74 -0
- package/dist/{index.js → cjs/index.js} +1 -1
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/rest-client.js +124 -0
- package/dist/{testUtils → cjs/test-utils}/asserts.js +1 -1
- package/dist/{testUtils → cjs/test-utils}/mocks.js +8 -4
- package/dist/cjs/test-utils/msw-setup.js +27 -0
- package/dist/{testUtils/testDefaults.js → cjs/test-utils/test-defaults.js} +41 -52
- package/dist/cjs/todoist-api.js +1235 -0
- package/dist/{types → cjs/types}/entities.js +78 -31
- package/dist/cjs/types/errors.js +22 -0
- package/dist/cjs/types/http.js +22 -0
- package/dist/cjs/utils/case-conversion.js +69 -0
- package/dist/{utils → cjs/utils}/colors.js +3 -3
- package/dist/cjs/utils/fetch-with-retry.js +150 -0
- package/dist/{utils → cjs/utils}/index.js +4 -4
- package/dist/cjs/utils/multipart-upload.js +126 -0
- package/dist/{utils → cjs/utils}/processing-helpers.js +3 -3
- package/dist/{utils → cjs/utils}/sanitization.js +17 -28
- package/dist/{utils/urlHelpers.js → cjs/utils/url-helpers.js} +15 -15
- package/dist/{utils → cjs/utils}/validators.js +28 -1
- package/dist/esm/authentication.js +151 -0
- package/dist/esm/consts/endpoints.js +65 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/rest-client.js +119 -0
- package/dist/esm/test-utils/asserts.js +8 -0
- package/dist/esm/test-utils/mocks.js +10 -0
- package/dist/esm/test-utils/msw-setup.js +22 -0
- package/dist/esm/test-utils/test-defaults.js +198 -0
- package/dist/esm/todoist-api.js +1231 -0
- package/dist/esm/types/entities.js +366 -0
- package/dist/esm/types/errors.js +18 -0
- package/dist/esm/types/http.js +18 -0
- package/dist/esm/types/index.js +3 -0
- package/dist/esm/types/requests.js +1 -0
- package/dist/esm/types/sync.js +1 -0
- package/dist/esm/utils/activity-helpers.js +36 -0
- package/dist/esm/utils/case-conversion.js +61 -0
- package/dist/esm/utils/colors.js +215 -0
- package/dist/esm/utils/fetch-with-retry.js +147 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/multipart-upload.js +120 -0
- package/dist/esm/utils/processing-helpers.js +12 -0
- package/dist/esm/utils/sanitization.js +112 -0
- package/dist/esm/utils/url-helpers.js +68 -0
- package/dist/esm/utils/validators.js +97 -0
- package/dist/{authentication.d.ts → types/authentication.d.ts} +6 -1
- package/dist/{consts → types/consts}/endpoints.d.ts +11 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/rest-client.d.ts +15 -0
- package/dist/types/test-utils/msw-setup.d.ts +3 -0
- package/dist/{TodoistApi.d.ts → types/todoist-api.d.ts} +91 -2
- package/dist/types/{entities.d.ts → types/entities.d.ts} +119 -0
- package/dist/types/types/http.d.ts +68 -0
- package/dist/types/types/index.d.ts +3 -0
- package/dist/types/{requests.d.ts → types/requests.d.ts} +137 -0
- package/dist/types/utils/case-conversion.d.ts +12 -0
- package/dist/types/utils/fetch-with-retry.d.ts +11 -0
- package/dist/types/utils/index.d.ts +3 -0
- package/dist/types/utils/multipart-upload.d.ts +50 -0
- package/dist/{utils → types/utils}/validators.d.ts +7 -1
- package/package.json +24 -8
- package/dist/TodoistApi.js +0 -1209
- package/dist/authentication.js +0 -199
- package/dist/consts/endpoints.js +0 -50
- package/dist/index.d.ts +0 -4
- package/dist/restClient.d.ts +0 -5
- package/dist/restClient.js +0 -170
- package/dist/types/errors.js +0 -39
- package/dist/types/http.d.ts +0 -1
- package/dist/types/http.js +0 -2
- package/dist/utils/index.d.ts +0 -3
- /package/dist/{types → cjs/types}/index.js +0 -0
- /package/dist/{types → cjs/types}/requests.js +0 -0
- /package/dist/{types → cjs/types}/sync.js +0 -0
- /package/dist/{utils → cjs/utils}/activity-helpers.js +0 -0
- /package/dist/{testUtils → types/test-utils}/asserts.d.ts +0 -0
- /package/dist/{testUtils → types/test-utils}/mocks.d.ts +0 -0
- /package/dist/{testUtils/testDefaults.d.ts → types/test-utils/test-defaults.d.ts} +0 -0
- /package/dist/types/{errors.d.ts → types/errors.d.ts} +0 -0
- /package/dist/types/{sync.d.ts → types/sync.d.ts} +0 -0
- /package/dist/{utils → types/utils}/activity-helpers.d.ts +0 -0
- /package/dist/{utils → types/utils}/colors.d.ts +0 -0
- /package/dist/{utils → types/utils}/processing-helpers.d.ts +0 -0
- /package/dist/{utils → types/utils}/sanitization.d.ts +0 -0
- /package/dist/{utils/urlHelpers.d.ts → types/utils/url-helpers.d.ts} +0 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { getProjectUrl, getTaskUrl, getSectionUrl } from '../utils/url-helpers.js';
|
|
14
|
+
export const DueDateSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
isRecurring: z.boolean(),
|
|
17
|
+
string: z.string(),
|
|
18
|
+
date: z.string(),
|
|
19
|
+
})
|
|
20
|
+
.extend({
|
|
21
|
+
datetime: z.string().nullable().optional(),
|
|
22
|
+
timezone: z.string().nullable().optional(),
|
|
23
|
+
lang: z.string().nullable().optional(),
|
|
24
|
+
});
|
|
25
|
+
export const DurationSchema = z.object({
|
|
26
|
+
amount: z.number().positive('Value should be greater than zero'),
|
|
27
|
+
unit: z.enum(['minute', 'day']),
|
|
28
|
+
});
|
|
29
|
+
export const DeadlineSchema = z.object({
|
|
30
|
+
date: z.string(),
|
|
31
|
+
lang: z.string(),
|
|
32
|
+
});
|
|
33
|
+
export const TaskSchema = z
|
|
34
|
+
.object({
|
|
35
|
+
id: z.string(),
|
|
36
|
+
userId: z.string(),
|
|
37
|
+
projectId: z.string(),
|
|
38
|
+
sectionId: z.string().nullable(),
|
|
39
|
+
parentId: z.string().nullable(),
|
|
40
|
+
addedByUid: z.string().nullable(),
|
|
41
|
+
assignedByUid: z.string().nullable(),
|
|
42
|
+
responsibleUid: z.string().nullable(),
|
|
43
|
+
labels: z.array(z.string()),
|
|
44
|
+
deadline: DeadlineSchema.nullable(),
|
|
45
|
+
duration: DurationSchema.nullable(),
|
|
46
|
+
checked: z.boolean(),
|
|
47
|
+
isDeleted: z.boolean(),
|
|
48
|
+
addedAt: z.string().nullable(),
|
|
49
|
+
completedAt: z.string().nullable(),
|
|
50
|
+
updatedAt: z.string().nullable(),
|
|
51
|
+
due: DueDateSchema.nullable(),
|
|
52
|
+
priority: z.number().int(),
|
|
53
|
+
childOrder: z.number().int(),
|
|
54
|
+
content: z.string(),
|
|
55
|
+
description: z.string(),
|
|
56
|
+
noteCount: z.number().int(),
|
|
57
|
+
dayOrder: z.number().int(),
|
|
58
|
+
isCollapsed: z.boolean(),
|
|
59
|
+
})
|
|
60
|
+
.transform((data) => {
|
|
61
|
+
return Object.assign(Object.assign({}, data), { url: getTaskUrl(data.id, data.content) });
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Base schema for all project types in Todoist.
|
|
65
|
+
* Contains common fields shared between personal and workspace projects.
|
|
66
|
+
*/
|
|
67
|
+
export const BaseProjectSchema = z.object({
|
|
68
|
+
id: z.string(),
|
|
69
|
+
canAssignTasks: z.boolean(),
|
|
70
|
+
childOrder: z.number().int(),
|
|
71
|
+
color: z.string(),
|
|
72
|
+
createdAt: z.string().nullable(),
|
|
73
|
+
isArchived: z.boolean(),
|
|
74
|
+
isDeleted: z.boolean(),
|
|
75
|
+
isFavorite: z.boolean(),
|
|
76
|
+
isFrozen: z.boolean(),
|
|
77
|
+
name: z.string(),
|
|
78
|
+
updatedAt: z.string().nullable(),
|
|
79
|
+
viewStyle: z.string(),
|
|
80
|
+
defaultOrder: z.number().int(),
|
|
81
|
+
description: z.string(),
|
|
82
|
+
isCollapsed: z.boolean(),
|
|
83
|
+
isShared: z.boolean(),
|
|
84
|
+
});
|
|
85
|
+
/**
|
|
86
|
+
* Schema for personal projects in Todoist.
|
|
87
|
+
*/
|
|
88
|
+
export const PersonalProjectSchema = BaseProjectSchema.extend({
|
|
89
|
+
parentId: z.string().nullable(),
|
|
90
|
+
inboxProject: z.boolean().optional().default(false),
|
|
91
|
+
}).transform((data) => {
|
|
92
|
+
return Object.assign(Object.assign({}, data), { url: getProjectUrl(data.id, data.name) });
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* Schema for workspace projects in Todoist.
|
|
96
|
+
*/
|
|
97
|
+
export const WorkspaceProjectSchema = BaseProjectSchema.extend({
|
|
98
|
+
collaboratorRoleDefault: z.string(),
|
|
99
|
+
folderId: z.string().nullable(),
|
|
100
|
+
isInviteOnly: z.boolean().nullable(),
|
|
101
|
+
isLinkSharingEnabled: z.boolean(),
|
|
102
|
+
role: z.string().nullable(),
|
|
103
|
+
status: z.string(),
|
|
104
|
+
workspaceId: z.string(),
|
|
105
|
+
}).transform((data) => {
|
|
106
|
+
return Object.assign(Object.assign({}, data), { url: getProjectUrl(data.id, data.name) });
|
|
107
|
+
});
|
|
108
|
+
export const SectionSchema = z
|
|
109
|
+
.object({
|
|
110
|
+
id: z.string(),
|
|
111
|
+
userId: z.string(),
|
|
112
|
+
projectId: z.string(),
|
|
113
|
+
addedAt: z.string(),
|
|
114
|
+
updatedAt: z.string(),
|
|
115
|
+
archivedAt: z.string().nullable(),
|
|
116
|
+
name: z.string(),
|
|
117
|
+
sectionOrder: z.number().int(),
|
|
118
|
+
isArchived: z.boolean(),
|
|
119
|
+
isDeleted: z.boolean(),
|
|
120
|
+
isCollapsed: z.boolean(),
|
|
121
|
+
})
|
|
122
|
+
.transform((data) => {
|
|
123
|
+
return Object.assign(Object.assign({}, data), { url: getSectionUrl(data.id, data.name) });
|
|
124
|
+
});
|
|
125
|
+
export const LabelSchema = z.object({
|
|
126
|
+
id: z.string(),
|
|
127
|
+
order: z.number().int().nullable(),
|
|
128
|
+
name: z.string(),
|
|
129
|
+
color: z.string(),
|
|
130
|
+
isFavorite: z.boolean(),
|
|
131
|
+
});
|
|
132
|
+
export const AttachmentSchema = z
|
|
133
|
+
.object({
|
|
134
|
+
resourceType: z.string(),
|
|
135
|
+
})
|
|
136
|
+
.extend({
|
|
137
|
+
fileName: z.string().nullable().optional(),
|
|
138
|
+
fileSize: z.number().int().nullable().optional(),
|
|
139
|
+
fileType: z.string().nullable().optional(),
|
|
140
|
+
fileUrl: z.string().nullable().optional(),
|
|
141
|
+
fileDuration: z.number().int().nullable().optional(),
|
|
142
|
+
uploadState: z.enum(['pending', 'completed']).nullable().optional(),
|
|
143
|
+
image: z.string().nullable().optional(),
|
|
144
|
+
imageWidth: z.number().int().nullable().optional(),
|
|
145
|
+
imageHeight: z.number().int().nullable().optional(),
|
|
146
|
+
url: z.string().nullable().optional(),
|
|
147
|
+
title: z.string().nullable().optional(),
|
|
148
|
+
});
|
|
149
|
+
export const RawCommentSchema = z
|
|
150
|
+
.object({
|
|
151
|
+
id: z.string(),
|
|
152
|
+
itemId: z.string().optional(),
|
|
153
|
+
projectId: z.string().optional(),
|
|
154
|
+
content: z.string(),
|
|
155
|
+
postedAt: z.string(),
|
|
156
|
+
fileAttachment: AttachmentSchema.nullable(),
|
|
157
|
+
postedUid: z.string(),
|
|
158
|
+
uidsToNotify: z.array(z.string()).nullable(),
|
|
159
|
+
reactions: z.record(z.string(), z.array(z.string())).nullable(),
|
|
160
|
+
isDeleted: z.boolean(),
|
|
161
|
+
})
|
|
162
|
+
.refine((data) => {
|
|
163
|
+
// At least one of itemId or projectId must be present
|
|
164
|
+
return ((data.itemId !== undefined && data.projectId === undefined) ||
|
|
165
|
+
(data.itemId === undefined && data.projectId !== undefined));
|
|
166
|
+
}, {
|
|
167
|
+
message: 'Exactly one of itemId or projectId must be provided',
|
|
168
|
+
});
|
|
169
|
+
export const CommentSchema = RawCommentSchema.transform((data) => {
|
|
170
|
+
const { itemId } = data, rest = __rest(data, ["itemId"]);
|
|
171
|
+
return Object.assign(Object.assign({}, rest), {
|
|
172
|
+
// Map itemId to taskId for backwards compatibility
|
|
173
|
+
taskId: itemId });
|
|
174
|
+
});
|
|
175
|
+
export const UserSchema = z.object({
|
|
176
|
+
id: z.string(),
|
|
177
|
+
name: z.string(),
|
|
178
|
+
email: z.string(),
|
|
179
|
+
});
|
|
180
|
+
export const TimezoneInfoSchema = z.object({
|
|
181
|
+
gmtString: z.string(),
|
|
182
|
+
hours: z.number().int(),
|
|
183
|
+
isDst: z.number().int(),
|
|
184
|
+
minutes: z.number().int(),
|
|
185
|
+
timezone: z.string(),
|
|
186
|
+
});
|
|
187
|
+
export const CurrentUserSchema = z.object({
|
|
188
|
+
id: z.string(),
|
|
189
|
+
email: z.string(),
|
|
190
|
+
fullName: z.string(),
|
|
191
|
+
avatarBig: z.string().nullish(),
|
|
192
|
+
avatarMedium: z.string().nullish(),
|
|
193
|
+
avatarS640: z.string().nullish(),
|
|
194
|
+
avatarSmall: z.string().nullish(),
|
|
195
|
+
businessAccountId: z.string().nullable(),
|
|
196
|
+
isPremium: z.boolean(),
|
|
197
|
+
dateFormat: z.number().int(),
|
|
198
|
+
timeFormat: z.number().int(),
|
|
199
|
+
weeklyGoal: z.number().int(),
|
|
200
|
+
dailyGoal: z.number().int(),
|
|
201
|
+
completedCount: z.number().int(),
|
|
202
|
+
completedToday: z.number().int(),
|
|
203
|
+
karma: z.number(),
|
|
204
|
+
karmaTrend: z.string(),
|
|
205
|
+
lang: z.string(),
|
|
206
|
+
nextWeek: z.number().int(),
|
|
207
|
+
startDay: z.number().int(),
|
|
208
|
+
startPage: z.string(),
|
|
209
|
+
tzInfo: TimezoneInfoSchema,
|
|
210
|
+
inboxProjectId: z.string(),
|
|
211
|
+
daysOff: z.array(z.number().int()),
|
|
212
|
+
weekendStartDay: z.number().int(),
|
|
213
|
+
});
|
|
214
|
+
const StreakSchema = z.object({
|
|
215
|
+
count: z.number(),
|
|
216
|
+
start: z.string(),
|
|
217
|
+
end: z.string(),
|
|
218
|
+
});
|
|
219
|
+
const CompletedItemSchema = z.object({
|
|
220
|
+
id: z.string(),
|
|
221
|
+
completed: z.number(),
|
|
222
|
+
});
|
|
223
|
+
const ItemsWithDateSchema = z.object({
|
|
224
|
+
items: z.array(CompletedItemSchema),
|
|
225
|
+
totalCompleted: z.number(),
|
|
226
|
+
});
|
|
227
|
+
const KarmaUpdateSchema = z.object({
|
|
228
|
+
time: z.string(),
|
|
229
|
+
newKarma: z.number(),
|
|
230
|
+
positiveKarma: z.number(),
|
|
231
|
+
negativeKarma: z.number(),
|
|
232
|
+
positiveKarmaReasons: z.array(z.any()),
|
|
233
|
+
negativeKarmaReasons: z.array(z.any()),
|
|
234
|
+
});
|
|
235
|
+
export const ProductivityStatsSchema = z.object({
|
|
236
|
+
completedCount: z.number(),
|
|
237
|
+
daysItems: z.array(ItemsWithDateSchema.extend({
|
|
238
|
+
date: z.string(),
|
|
239
|
+
})),
|
|
240
|
+
goals: z.object({
|
|
241
|
+
currentDailyStreak: StreakSchema,
|
|
242
|
+
currentWeeklyStreak: StreakSchema,
|
|
243
|
+
dailyGoal: z.number(),
|
|
244
|
+
ignoreDays: z.array(z.number()),
|
|
245
|
+
karmaDisabled: z.number(),
|
|
246
|
+
lastDailyStreak: StreakSchema,
|
|
247
|
+
lastWeeklyStreak: StreakSchema,
|
|
248
|
+
maxDailyStreak: StreakSchema,
|
|
249
|
+
maxWeeklyStreak: StreakSchema,
|
|
250
|
+
user: z.string(),
|
|
251
|
+
userId: z.string(),
|
|
252
|
+
vacationMode: z.number(),
|
|
253
|
+
weeklyGoal: z.number(),
|
|
254
|
+
}),
|
|
255
|
+
karma: z.number(),
|
|
256
|
+
karmaGraphData: z.array(z.object({
|
|
257
|
+
date: z.string(),
|
|
258
|
+
karmaAvg: z.number(),
|
|
259
|
+
})),
|
|
260
|
+
karmaLastUpdate: z.number(),
|
|
261
|
+
karmaTrend: z.string(),
|
|
262
|
+
karmaUpdateReasons: z.array(KarmaUpdateSchema),
|
|
263
|
+
projectColors: z.record(z.string(), z.string()),
|
|
264
|
+
weekItems: z.array(ItemsWithDateSchema.extend({
|
|
265
|
+
from: z.string(),
|
|
266
|
+
to: z.string(),
|
|
267
|
+
})),
|
|
268
|
+
});
|
|
269
|
+
export const ColorSchema = z.object({
|
|
270
|
+
/** @deprecated No longer used */
|
|
271
|
+
id: z.number(),
|
|
272
|
+
/** The key of the color (i.e. 'berry_red') */
|
|
273
|
+
key: z.string(),
|
|
274
|
+
/** The display name of the color (i.e. 'Berry Red') */
|
|
275
|
+
displayName: z.string(),
|
|
276
|
+
/** @deprecated Use {@link Color.displayName} instead */
|
|
277
|
+
name: z.string(),
|
|
278
|
+
/** The hex value of the color (i.e. '#b8255f') */
|
|
279
|
+
hexValue: z.string(),
|
|
280
|
+
/**
|
|
281
|
+
* @deprecated Use {@link Color.hexValue} instead
|
|
282
|
+
*/
|
|
283
|
+
value: z.string(),
|
|
284
|
+
});
|
|
285
|
+
/**
|
|
286
|
+
* Flexible object containing event-specific data.
|
|
287
|
+
* Uses z.record to accept any properties for forward compatibility.
|
|
288
|
+
*/
|
|
289
|
+
export const ActivityEventExtraDataSchema = z.record(z.string(), z.any()).nullable();
|
|
290
|
+
/**
|
|
291
|
+
* Activity log event schema. Accepts unknown fields for forward compatibility.
|
|
292
|
+
*/
|
|
293
|
+
export const ActivityEventSchema = z
|
|
294
|
+
.object({
|
|
295
|
+
objectType: z.string(),
|
|
296
|
+
objectId: z.string(),
|
|
297
|
+
eventType: z.string(),
|
|
298
|
+
eventDate: z.string(),
|
|
299
|
+
id: z
|
|
300
|
+
.union([z.string(), z.number()])
|
|
301
|
+
.transform((val) => { var _a; return (_a = val === null || val === void 0 ? void 0 : val.toString()) !== null && _a !== void 0 ? _a : null; })
|
|
302
|
+
.nullable(),
|
|
303
|
+
parentProjectId: z.string().nullable(),
|
|
304
|
+
parentItemId: z.string().nullable(),
|
|
305
|
+
initiatorId: z.string().nullable(),
|
|
306
|
+
extraData: ActivityEventExtraDataSchema,
|
|
307
|
+
})
|
|
308
|
+
.catchall(z.any());
|
|
309
|
+
/**
|
|
310
|
+
* Available workspace roles.
|
|
311
|
+
*/
|
|
312
|
+
export const WORKSPACE_ROLES = ['ADMIN', 'MEMBER', 'GUEST'];
|
|
313
|
+
export const WorkspaceRoleSchema = z.enum(WORKSPACE_ROLES);
|
|
314
|
+
export const WorkspaceUserSchema = z.object({
|
|
315
|
+
userId: z.string(),
|
|
316
|
+
workspaceId: z.string(),
|
|
317
|
+
userEmail: z.string(),
|
|
318
|
+
fullName: z.string(),
|
|
319
|
+
timezone: z.string(),
|
|
320
|
+
role: WorkspaceRoleSchema,
|
|
321
|
+
imageId: z.string().nullable(),
|
|
322
|
+
isDeleted: z.boolean().default(false),
|
|
323
|
+
});
|
|
324
|
+
export const WorkspaceInvitationSchema = z.object({
|
|
325
|
+
id: z.string().default('0'),
|
|
326
|
+
inviterId: z.string(),
|
|
327
|
+
userEmail: z.string(),
|
|
328
|
+
workspaceId: z.string(),
|
|
329
|
+
role: WorkspaceRoleSchema,
|
|
330
|
+
isExistingUser: z.boolean(),
|
|
331
|
+
});
|
|
332
|
+
export const PlanPriceSchema = z.object({
|
|
333
|
+
currency: z.string(),
|
|
334
|
+
amount: z.union([z.number(), z.string()]),
|
|
335
|
+
interval: z.string().optional(),
|
|
336
|
+
});
|
|
337
|
+
export const FormattedPriceListingSchema = z.object({
|
|
338
|
+
currency: z.string().optional(),
|
|
339
|
+
amount: z.number().optional(),
|
|
340
|
+
interval: z.string().optional(),
|
|
341
|
+
formatted: z.string().optional(),
|
|
342
|
+
});
|
|
343
|
+
export const WorkspacePlanDetailsSchema = z.object({
|
|
344
|
+
currentMemberCount: z.number(),
|
|
345
|
+
currentPlan: z.enum(['Business', 'Starter']),
|
|
346
|
+
currentPlanStatus: z.enum(['Active', 'Downgraded', 'Cancelled', 'NeverSubscribed']),
|
|
347
|
+
downgradeAt: z.string().nullable(),
|
|
348
|
+
currentActiveProjects: z.number(),
|
|
349
|
+
maximumActiveProjects: z.number(),
|
|
350
|
+
priceList: z.array(FormattedPriceListingSchema),
|
|
351
|
+
workspaceId: z.number(),
|
|
352
|
+
isTrialing: z.boolean(),
|
|
353
|
+
trialEndsAt: z.string().nullable(),
|
|
354
|
+
cancelAtPeriodEnd: z.boolean(),
|
|
355
|
+
hasTrialed: z.boolean(),
|
|
356
|
+
planPrice: PlanPriceSchema.nullable(),
|
|
357
|
+
hasBillingPortal: z.boolean(),
|
|
358
|
+
hasBillingPortalSwitchToAnnual: z.boolean(),
|
|
359
|
+
});
|
|
360
|
+
export const JoinWorkspaceResultSchema = z.object({
|
|
361
|
+
custom_sorting_applied: z.boolean(),
|
|
362
|
+
project_sort_preference: z.string(),
|
|
363
|
+
role: WorkspaceRoleSchema,
|
|
364
|
+
user_id: z.string(),
|
|
365
|
+
workspace_id: z.string(),
|
|
366
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CustomError } from 'ts-custom-error';
|
|
2
|
+
const authenticationErrorCodes = [401, 403];
|
|
3
|
+
export class TodoistRequestError extends CustomError {
|
|
4
|
+
// eslint-disable-next-line max-params
|
|
5
|
+
constructor(message, httpStatusCode, responseData) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.message = message;
|
|
8
|
+
this.httpStatusCode = httpStatusCode;
|
|
9
|
+
this.responseData = responseData;
|
|
10
|
+
this.isAuthenticationError = () => {
|
|
11
|
+
if (!this.httpStatusCode) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return authenticationErrorCodes.includes(this.httpStatusCode);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(this, 'name', { value: 'TodoistRequestError' });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard to check if an error is a network error
|
|
3
|
+
*/
|
|
4
|
+
export function isNetworkError(error) {
|
|
5
|
+
// Network errors in fetch are typically TypeError with specific messages
|
|
6
|
+
return ((error instanceof TypeError &&
|
|
7
|
+
(error.message.includes('fetch') ||
|
|
8
|
+
error.message.includes('network') ||
|
|
9
|
+
error.message.includes('Failed to fetch') ||
|
|
10
|
+
error.message.includes('NetworkError'))) ||
|
|
11
|
+
error.isNetworkError === true);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Type guard to check if an error is an HTTP error
|
|
15
|
+
*/
|
|
16
|
+
export function isHttpError(error) {
|
|
17
|
+
return 'status' in error && typeof error.status === 'number';
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts modern SDK object type naming to legacy API naming.
|
|
3
|
+
* Maps 'task' -> 'item' and 'comment' -> 'note' for API requests.
|
|
4
|
+
* Other values pass through unchanged.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
* @param objectType The object type using modern naming.
|
|
8
|
+
* @returns The object type using legacy API naming.
|
|
9
|
+
*/
|
|
10
|
+
export function normalizeObjectTypeForApi(objectType) {
|
|
11
|
+
if (!objectType)
|
|
12
|
+
return objectType;
|
|
13
|
+
if (objectType === 'task')
|
|
14
|
+
return 'item';
|
|
15
|
+
if (objectType === 'comment')
|
|
16
|
+
return 'note';
|
|
17
|
+
return objectType;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Converts legacy API object type naming to modern SDK naming.
|
|
21
|
+
* Maps 'item' -> 'task' and 'note' -> 'comment' for SDK responses.
|
|
22
|
+
* Other values pass through unchanged.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
* @param objectType The object type using legacy API naming.
|
|
26
|
+
* @returns The object type using modern SDK naming.
|
|
27
|
+
*/
|
|
28
|
+
export function denormalizeObjectTypeFromApi(objectType) {
|
|
29
|
+
if (!objectType)
|
|
30
|
+
return objectType;
|
|
31
|
+
if (objectType === 'item')
|
|
32
|
+
return 'task';
|
|
33
|
+
if (objectType === 'note')
|
|
34
|
+
return 'comment';
|
|
35
|
+
return objectType;
|
|
36
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import camelcase from 'camelcase';
|
|
2
|
+
import emojiRegex from 'emoji-regex';
|
|
3
|
+
/**
|
|
4
|
+
* Checks if a string is a solitary emoji
|
|
5
|
+
*/
|
|
6
|
+
function isEmojiKey(input) {
|
|
7
|
+
const regex = emojiRegex();
|
|
8
|
+
const match = input.match(regex);
|
|
9
|
+
return match !== null && match.join('') === input;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Custom camelCase function that preserves emoji strings
|
|
13
|
+
*/
|
|
14
|
+
export function customCamelCase(input) {
|
|
15
|
+
// If the value is a solitary emoji string, return the key as-is
|
|
16
|
+
if (isEmojiKey(input)) {
|
|
17
|
+
return input;
|
|
18
|
+
}
|
|
19
|
+
return camelcase(input);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Converts object keys from snake_case to camelCase recursively
|
|
23
|
+
*/
|
|
24
|
+
export function camelCaseKeys(obj) {
|
|
25
|
+
if (obj === null || obj === undefined) {
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(obj)) {
|
|
29
|
+
return obj.map((item) => camelCaseKeys(item));
|
|
30
|
+
}
|
|
31
|
+
if (typeof obj === 'object' && obj.constructor === Object) {
|
|
32
|
+
const converted = {};
|
|
33
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
34
|
+
const camelKey = customCamelCase(key);
|
|
35
|
+
converted[camelKey] = camelCaseKeys(value);
|
|
36
|
+
}
|
|
37
|
+
return converted;
|
|
38
|
+
}
|
|
39
|
+
return obj;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Converts object keys from camelCase to snake_case recursively
|
|
43
|
+
*/
|
|
44
|
+
export function snakeCaseKeys(obj) {
|
|
45
|
+
if (obj === null || obj === undefined) {
|
|
46
|
+
return obj;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(obj)) {
|
|
49
|
+
return obj.map((item) => snakeCaseKeys(item));
|
|
50
|
+
}
|
|
51
|
+
if (typeof obj === 'object' && obj.constructor === Object) {
|
|
52
|
+
const converted = {};
|
|
53
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
54
|
+
// Convert camelCase to snake_case
|
|
55
|
+
const snakeKey = key.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
56
|
+
converted[snakeKey] = snakeCaseKeys(value);
|
|
57
|
+
}
|
|
58
|
+
return converted;
|
|
59
|
+
}
|
|
60
|
+
return obj;
|
|
61
|
+
}
|