@doist/todoist-sdk 9.4.0 → 9.5.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/dist/cjs/test-utils/webhook-fixtures.js +32 -0
- package/dist/cjs/types/webhooks/comments.js +96 -0
- package/dist/cjs/types/webhooks/index.js +2 -0
- package/dist/cjs/types/webhooks/labels.js +54 -0
- package/dist/cjs/types/webhooks/types.js +25 -5
- package/dist/esm/test-utils/webhook-fixtures.js +26 -0
- package/dist/esm/types/webhooks/comments.js +93 -0
- package/dist/esm/types/webhooks/index.js +2 -0
- package/dist/esm/types/webhooks/labels.js +51 -0
- package/dist/esm/types/webhooks/types.js +25 -5
- package/dist/types/test-utils/webhook-fixtures.d.ts +13 -0
- package/dist/types/types/webhooks/comments.d.ts +1600 -0
- package/dist/types/types/webhooks/index.d.ts +2 -0
- package/dist/types/types/webhooks/labels.d.ts +160 -0
- package/dist/types/types/webhooks/types.d.ts +1059 -16
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CLIENT_SECRET = void 0;
|
|
4
|
+
exports.sign = sign;
|
|
5
|
+
exports.rawTask = rawTask;
|
|
6
|
+
exports.envelope = envelope;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
exports.CLIENT_SECRET = 'test-client-secret';
|
|
9
|
+
/** HMAC-SHA256 signs a raw body with the test client secret (or an override). */
|
|
10
|
+
function sign(body, secret = exports.CLIENT_SECRET) {
|
|
11
|
+
return (0, node_crypto_1.createHmac)('sha256', secret).update(body).digest('base64');
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Snake_case task payload matching the v1 webhook wire shape.
|
|
15
|
+
*/
|
|
16
|
+
function rawTask(overrides = {}) {
|
|
17
|
+
return Object.assign({ id: '6XR4GqQQCW6Gv9h4', user_id: '2671355', project_id: '6XR4H993xv8H5qCR', section_id: null, parent_id: null, added_by_uid: '2671355', assigned_by_uid: null, responsible_uid: null, labels: [], deadline: null, duration: null, is_collapsed: false, checked: false, is_deleted: false, added_at: '2025-02-10T10:33:38.000000Z', completed_at: null, completed_by_uid: null, updated_at: '2025-02-10T10:33:38.000000Z', due: null, priority: 1, child_order: 3, content: 'Buy Milk', description: '', note_count: 0, day_order: -1, goal_ids: [], url: 'https://app.todoist.com/app/task/6XR4GqQQCW6Gv9h4' }, overrides);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Wraps an event's `event_data` in the snake_case envelope the backend sends
|
|
21
|
+
* (initiator, version, timestamps, etc). `extra` passes extra top-level keys
|
|
22
|
+
* like `event_data_extra`.
|
|
23
|
+
*/
|
|
24
|
+
function envelope(event, eventData, extra) {
|
|
25
|
+
return Object.assign({ version: '1', user_id: '2671355', initiator: {
|
|
26
|
+
id: '2671355',
|
|
27
|
+
full_name: 'Alice',
|
|
28
|
+
email: 'alice@example.com',
|
|
29
|
+
image_id: 'ad38375bdb094286af59f1eab36d8f20',
|
|
30
|
+
is_premium: true,
|
|
31
|
+
}, event_name: event, event_data: eventData, triggered_at: '2025-02-10T10:39:38.000000Z' }, extra);
|
|
32
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.CommentDeletedPayloadSchema = exports.CommentUpdatedPayloadSchema = exports.CommentAddedPayloadSchema = exports.WebhookCommentSchema = exports.WebhookProjectCommentSchema = exports.WebhookItemCommentSchema = exports.COMMENT_WEBHOOK_EVENTS = exports.CommentWebhookEventEnum = void 0;
|
|
15
|
+
const zod_1 = require("zod");
|
|
16
|
+
const types_1 = require("../comments/types");
|
|
17
|
+
const types_2 = require("../tasks/types");
|
|
18
|
+
const envelope_1 = require("./envelope");
|
|
19
|
+
/**
|
|
20
|
+
* Enum-like object of comment (`note:*`) webhook event names. Single source
|
|
21
|
+
* of truth for the discriminator values — `COMMENT_WEBHOOK_EVENTS` and every
|
|
22
|
+
* `z.literal()` below derive from it.
|
|
23
|
+
*
|
|
24
|
+
* The on-the-wire event names remain `note:*` for historical reasons; the
|
|
25
|
+
* SDK types use `Comment*` to match the rest of the public API where the
|
|
26
|
+
* resource is named `Comment`.
|
|
27
|
+
*/
|
|
28
|
+
exports.CommentWebhookEventEnum = {
|
|
29
|
+
Added: 'note:added',
|
|
30
|
+
Updated: 'note:updated',
|
|
31
|
+
Deleted: 'note:deleted',
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Webhook events that carry a comment as their `eventData`.
|
|
35
|
+
*/
|
|
36
|
+
exports.COMMENT_WEBHOOK_EVENTS = [
|
|
37
|
+
exports.CommentWebhookEventEnum.Added,
|
|
38
|
+
exports.CommentWebhookEventEnum.Updated,
|
|
39
|
+
exports.CommentWebhookEventEnum.Deleted,
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Fields common to both item and project comment payloads.
|
|
43
|
+
*
|
|
44
|
+
* `postedAt` and `postedUid` are nullable here (unlike
|
|
45
|
+
* {@link RawCommentSchema}) because webhook payloads may omit them — for
|
|
46
|
+
* example when the poster could not be resolved, or on a deleted-note
|
|
47
|
+
* tombstone.
|
|
48
|
+
*/
|
|
49
|
+
const WebhookCommentBaseSchema = zod_1.z.object({
|
|
50
|
+
id: zod_1.z.string(),
|
|
51
|
+
content: zod_1.z.string(),
|
|
52
|
+
postedAt: zod_1.z.coerce.date().nullable(),
|
|
53
|
+
postedUid: zod_1.z.string().nullable(),
|
|
54
|
+
fileAttachment: types_1.AttachmentSchema.nullable(),
|
|
55
|
+
uidsToNotify: zod_1.z.array(zod_1.z.string()).nullable(),
|
|
56
|
+
reactions: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string())).nullable(),
|
|
57
|
+
isDeleted: zod_1.z.boolean(),
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* A comment attached to a task, delivered in a webhook payload.
|
|
61
|
+
*
|
|
62
|
+
* Webhook item-comment payloads carry two fields that don't appear on the
|
|
63
|
+
* REST comment resource: `url` (the direct task URL) and `item` (the full
|
|
64
|
+
* embedded {@link Task}). Both are parsed here so consumers handling a
|
|
65
|
+
* `note:added` event also get the task it belongs to.
|
|
66
|
+
*
|
|
67
|
+
* `itemId` is exposed as `taskId` to match the rest of the SDK surface.
|
|
68
|
+
*/
|
|
69
|
+
exports.WebhookItemCommentSchema = WebhookCommentBaseSchema.extend({
|
|
70
|
+
itemId: zod_1.z.string(),
|
|
71
|
+
url: zod_1.z.string(),
|
|
72
|
+
item: types_2.TaskSchema,
|
|
73
|
+
})
|
|
74
|
+
.strict()
|
|
75
|
+
.transform((_a) => {
|
|
76
|
+
var { itemId } = _a, rest = __rest(_a, ["itemId"]);
|
|
77
|
+
return (Object.assign(Object.assign({}, rest), { taskId: itemId }));
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* A comment attached to a project, delivered in a webhook payload.
|
|
81
|
+
*
|
|
82
|
+
* Project comments do not carry the extra `url` or embedded project fields
|
|
83
|
+
* that item comments carry for the parent task.
|
|
84
|
+
*/
|
|
85
|
+
exports.WebhookProjectCommentSchema = WebhookCommentBaseSchema.extend({
|
|
86
|
+
projectId: zod_1.z.string(),
|
|
87
|
+
}).strict();
|
|
88
|
+
/**
|
|
89
|
+
* The `eventData` of a `note:*` webhook payload — either an item comment
|
|
90
|
+
* (includes `taskId` and an embedded {@link Task}) or a project comment
|
|
91
|
+
* (includes `projectId`). Narrow with `'taskId' in eventData`.
|
|
92
|
+
*/
|
|
93
|
+
exports.WebhookCommentSchema = zod_1.z.union([exports.WebhookItemCommentSchema, exports.WebhookProjectCommentSchema]);
|
|
94
|
+
exports.CommentAddedPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.literal(exports.CommentWebhookEventEnum.Added), eventData: exports.WebhookCommentSchema }));
|
|
95
|
+
exports.CommentUpdatedPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.literal(exports.CommentWebhookEventEnum.Updated), eventData: exports.WebhookCommentSchema }));
|
|
96
|
+
exports.CommentDeletedPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.literal(exports.CommentWebhookEventEnum.Deleted), eventData: exports.WebhookCommentSchema }));
|
|
@@ -14,6 +14,8 @@ 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
|
+
__exportStar(require("./comments"), exports);
|
|
17
18
|
__exportStar(require("./envelope"), exports);
|
|
19
|
+
__exportStar(require("./labels"), exports);
|
|
18
20
|
__exportStar(require("./tasks"), exports);
|
|
19
21
|
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.LabelDeletedPayloadSchema = exports.LabelUpdatedPayloadSchema = exports.LabelAddedPayloadSchema = exports.WebhookLabelSchema = exports.LABEL_WEBHOOK_EVENTS = exports.LabelWebhookEventEnum = void 0;
|
|
15
|
+
const zod_1 = require("zod");
|
|
16
|
+
const types_1 = require("../labels/types");
|
|
17
|
+
const envelope_1 = require("./envelope");
|
|
18
|
+
/**
|
|
19
|
+
* Enum-like object of label (`label:*`) webhook event names. Single source
|
|
20
|
+
* of truth for the discriminator values — `LABEL_WEBHOOK_EVENTS` and every
|
|
21
|
+
* `z.literal()` below derive from it.
|
|
22
|
+
*/
|
|
23
|
+
exports.LabelWebhookEventEnum = {
|
|
24
|
+
Added: 'label:added',
|
|
25
|
+
Updated: 'label:updated',
|
|
26
|
+
Deleted: 'label:deleted',
|
|
27
|
+
};
|
|
28
|
+
/** Webhook events that carry a label as their `eventData`. */
|
|
29
|
+
exports.LABEL_WEBHOOK_EVENTS = [
|
|
30
|
+
exports.LabelWebhookEventEnum.Added,
|
|
31
|
+
exports.LabelWebhookEventEnum.Updated,
|
|
32
|
+
exports.LabelWebhookEventEnum.Deleted,
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* A label as delivered in a webhook payload.
|
|
36
|
+
*
|
|
37
|
+
* Webhook label payloads differ from the REST label shape in two ways:
|
|
38
|
+
* they use `item_order` (not `order`) and always include `is_deleted`. This
|
|
39
|
+
* schema reuses {@link LabelSchema}, swapping `order` for `itemOrder` on
|
|
40
|
+
* the input side and renaming it back after validation, and surfaces
|
|
41
|
+
* `isDeleted` for downstream filtering.
|
|
42
|
+
*/
|
|
43
|
+
exports.WebhookLabelSchema = types_1.LabelSchema.omit({ order: true })
|
|
44
|
+
.extend({
|
|
45
|
+
itemOrder: zod_1.z.number().int().nullable(),
|
|
46
|
+
isDeleted: zod_1.z.boolean(),
|
|
47
|
+
})
|
|
48
|
+
.transform((_a) => {
|
|
49
|
+
var { itemOrder } = _a, rest = __rest(_a, ["itemOrder"]);
|
|
50
|
+
return (Object.assign(Object.assign({}, rest), { order: itemOrder }));
|
|
51
|
+
});
|
|
52
|
+
exports.LabelAddedPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.literal(exports.LabelWebhookEventEnum.Added), eventData: exports.WebhookLabelSchema }));
|
|
53
|
+
exports.LabelUpdatedPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.literal(exports.LabelWebhookEventEnum.Updated), eventData: exports.WebhookLabelSchema }));
|
|
54
|
+
exports.LabelDeletedPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.literal(exports.LabelWebhookEventEnum.Deleted), eventData: exports.WebhookLabelSchema }));
|
|
@@ -4,13 +4,25 @@ exports.WebhookEventSchema = exports.WebhookPayloadSchema = exports.UntypedWebho
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const types_1 = require("../apps/types");
|
|
6
6
|
Object.defineProperty(exports, "WebhookEventSchema", { enumerable: true, get: function () { return types_1.WebhookEventSchema; } });
|
|
7
|
+
const comments_1 = require("./comments");
|
|
7
8
|
const envelope_1 = require("./envelope");
|
|
9
|
+
const labels_1 = require("./labels");
|
|
8
10
|
const tasks_1 = require("./tasks");
|
|
9
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Events whose payloads have been narrowed per resource. Each phase adds to
|
|
13
|
+
* this list; {@link UntypedWebhookPayloadSchema} below is derived by taking
|
|
14
|
+
* every {@link WebhookEvent} not present here.
|
|
15
|
+
*/
|
|
16
|
+
const TYPED_WEBHOOK_EVENTS = [
|
|
17
|
+
...tasks_1.TASK_WEBHOOK_EVENTS,
|
|
18
|
+
...comments_1.COMMENT_WEBHOOK_EVENTS,
|
|
19
|
+
...labels_1.LABEL_WEBHOOK_EVENTS,
|
|
20
|
+
];
|
|
21
|
+
const untypedEventNames = types_1.WEBHOOK_EVENTS.filter((event) => !TYPED_WEBHOOK_EVENTS.includes(event));
|
|
10
22
|
/**
|
|
11
23
|
* A single branch covering every event whose payload has not yet been typed
|
|
12
24
|
* per resource. Follow-up PRs will replace slices of this with dedicated
|
|
13
|
-
* variants (
|
|
25
|
+
* variants (projects, sections, filters, reminders).
|
|
14
26
|
*/
|
|
15
27
|
exports.UntypedWebhookPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.enum(untypedEventNames), eventData: zod_1.z.unknown(), eventDataExtra: zod_1.z.unknown().optional() }));
|
|
16
28
|
/**
|
|
@@ -20,9 +32,11 @@ exports.UntypedWebhookPayloadSchema = zod_1.z.object(Object.assign(Object.assign
|
|
|
20
32
|
* (`item:added`) in the `event_name` field, so consumers using
|
|
21
33
|
* `parseWebhookPayload` receive camelCase fields directly.
|
|
22
34
|
*
|
|
23
|
-
* Typed today: `item:*` events carry a parsed {@link Task}
|
|
24
|
-
*
|
|
25
|
-
*
|
|
35
|
+
* Typed today: `item:*` events carry a parsed {@link Task}; `note:*` events
|
|
36
|
+
* carry a parsed comment (item-comment or project-comment); `label:*` events
|
|
37
|
+
* carry a parsed label. Other events still expose `eventData` as `unknown`;
|
|
38
|
+
* they will be narrowed in follow-up PRs (projects, sections, filters,
|
|
39
|
+
* reminders).
|
|
26
40
|
*/
|
|
27
41
|
exports.WebhookPayloadSchema = zod_1.z.discriminatedUnion('eventName', [
|
|
28
42
|
tasks_1.TaskAddedPayloadSchema,
|
|
@@ -30,5 +44,11 @@ exports.WebhookPayloadSchema = zod_1.z.discriminatedUnion('eventName', [
|
|
|
30
44
|
tasks_1.TaskCompletedPayloadSchema,
|
|
31
45
|
tasks_1.TaskUncompletedPayloadSchema,
|
|
32
46
|
tasks_1.TaskDeletedPayloadSchema,
|
|
47
|
+
comments_1.CommentAddedPayloadSchema,
|
|
48
|
+
comments_1.CommentUpdatedPayloadSchema,
|
|
49
|
+
comments_1.CommentDeletedPayloadSchema,
|
|
50
|
+
labels_1.LabelAddedPayloadSchema,
|
|
51
|
+
labels_1.LabelUpdatedPayloadSchema,
|
|
52
|
+
labels_1.LabelDeletedPayloadSchema,
|
|
33
53
|
exports.UntypedWebhookPayloadSchema,
|
|
34
54
|
]);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createHmac } from 'node:crypto';
|
|
2
|
+
export const CLIENT_SECRET = 'test-client-secret';
|
|
3
|
+
/** HMAC-SHA256 signs a raw body with the test client secret (or an override). */
|
|
4
|
+
export function sign(body, secret = CLIENT_SECRET) {
|
|
5
|
+
return createHmac('sha256', secret).update(body).digest('base64');
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Snake_case task payload matching the v1 webhook wire shape.
|
|
9
|
+
*/
|
|
10
|
+
export function rawTask(overrides = {}) {
|
|
11
|
+
return Object.assign({ id: '6XR4GqQQCW6Gv9h4', user_id: '2671355', project_id: '6XR4H993xv8H5qCR', section_id: null, parent_id: null, added_by_uid: '2671355', assigned_by_uid: null, responsible_uid: null, labels: [], deadline: null, duration: null, is_collapsed: false, checked: false, is_deleted: false, added_at: '2025-02-10T10:33:38.000000Z', completed_at: null, completed_by_uid: null, updated_at: '2025-02-10T10:33:38.000000Z', due: null, priority: 1, child_order: 3, content: 'Buy Milk', description: '', note_count: 0, day_order: -1, goal_ids: [], url: 'https://app.todoist.com/app/task/6XR4GqQQCW6Gv9h4' }, overrides);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Wraps an event's `event_data` in the snake_case envelope the backend sends
|
|
15
|
+
* (initiator, version, timestamps, etc). `extra` passes extra top-level keys
|
|
16
|
+
* like `event_data_extra`.
|
|
17
|
+
*/
|
|
18
|
+
export function envelope(event, eventData, extra) {
|
|
19
|
+
return Object.assign({ version: '1', user_id: '2671355', initiator: {
|
|
20
|
+
id: '2671355',
|
|
21
|
+
full_name: 'Alice',
|
|
22
|
+
email: 'alice@example.com',
|
|
23
|
+
image_id: 'ad38375bdb094286af59f1eab36d8f20',
|
|
24
|
+
is_premium: true,
|
|
25
|
+
}, event_name: event, event_data: eventData, triggered_at: '2025-02-10T10:39:38.000000Z' }, extra);
|
|
26
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
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 { AttachmentSchema } from '../comments/types.js';
|
|
14
|
+
import { TaskSchema } from '../tasks/types.js';
|
|
15
|
+
import { BaseWebhookEnvelopeShape } from './envelope.js';
|
|
16
|
+
/**
|
|
17
|
+
* Enum-like object of comment (`note:*`) webhook event names. Single source
|
|
18
|
+
* of truth for the discriminator values — `COMMENT_WEBHOOK_EVENTS` and every
|
|
19
|
+
* `z.literal()` below derive from it.
|
|
20
|
+
*
|
|
21
|
+
* The on-the-wire event names remain `note:*` for historical reasons; the
|
|
22
|
+
* SDK types use `Comment*` to match the rest of the public API where the
|
|
23
|
+
* resource is named `Comment`.
|
|
24
|
+
*/
|
|
25
|
+
export const CommentWebhookEventEnum = {
|
|
26
|
+
Added: 'note:added',
|
|
27
|
+
Updated: 'note:updated',
|
|
28
|
+
Deleted: 'note:deleted',
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Webhook events that carry a comment as their `eventData`.
|
|
32
|
+
*/
|
|
33
|
+
export const COMMENT_WEBHOOK_EVENTS = [
|
|
34
|
+
CommentWebhookEventEnum.Added,
|
|
35
|
+
CommentWebhookEventEnum.Updated,
|
|
36
|
+
CommentWebhookEventEnum.Deleted,
|
|
37
|
+
];
|
|
38
|
+
/**
|
|
39
|
+
* Fields common to both item and project comment payloads.
|
|
40
|
+
*
|
|
41
|
+
* `postedAt` and `postedUid` are nullable here (unlike
|
|
42
|
+
* {@link RawCommentSchema}) because webhook payloads may omit them — for
|
|
43
|
+
* example when the poster could not be resolved, or on a deleted-note
|
|
44
|
+
* tombstone.
|
|
45
|
+
*/
|
|
46
|
+
const WebhookCommentBaseSchema = z.object({
|
|
47
|
+
id: z.string(),
|
|
48
|
+
content: z.string(),
|
|
49
|
+
postedAt: z.coerce.date().nullable(),
|
|
50
|
+
postedUid: z.string().nullable(),
|
|
51
|
+
fileAttachment: AttachmentSchema.nullable(),
|
|
52
|
+
uidsToNotify: z.array(z.string()).nullable(),
|
|
53
|
+
reactions: z.record(z.string(), z.array(z.string())).nullable(),
|
|
54
|
+
isDeleted: z.boolean(),
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* A comment attached to a task, delivered in a webhook payload.
|
|
58
|
+
*
|
|
59
|
+
* Webhook item-comment payloads carry two fields that don't appear on the
|
|
60
|
+
* REST comment resource: `url` (the direct task URL) and `item` (the full
|
|
61
|
+
* embedded {@link Task}). Both are parsed here so consumers handling a
|
|
62
|
+
* `note:added` event also get the task it belongs to.
|
|
63
|
+
*
|
|
64
|
+
* `itemId` is exposed as `taskId` to match the rest of the SDK surface.
|
|
65
|
+
*/
|
|
66
|
+
export const WebhookItemCommentSchema = WebhookCommentBaseSchema.extend({
|
|
67
|
+
itemId: z.string(),
|
|
68
|
+
url: z.string(),
|
|
69
|
+
item: TaskSchema,
|
|
70
|
+
})
|
|
71
|
+
.strict()
|
|
72
|
+
.transform((_a) => {
|
|
73
|
+
var { itemId } = _a, rest = __rest(_a, ["itemId"]);
|
|
74
|
+
return (Object.assign(Object.assign({}, rest), { taskId: itemId }));
|
|
75
|
+
});
|
|
76
|
+
/**
|
|
77
|
+
* A comment attached to a project, delivered in a webhook payload.
|
|
78
|
+
*
|
|
79
|
+
* Project comments do not carry the extra `url` or embedded project fields
|
|
80
|
+
* that item comments carry for the parent task.
|
|
81
|
+
*/
|
|
82
|
+
export const WebhookProjectCommentSchema = WebhookCommentBaseSchema.extend({
|
|
83
|
+
projectId: z.string(),
|
|
84
|
+
}).strict();
|
|
85
|
+
/**
|
|
86
|
+
* The `eventData` of a `note:*` webhook payload — either an item comment
|
|
87
|
+
* (includes `taskId` and an embedded {@link Task}) or a project comment
|
|
88
|
+
* (includes `projectId`). Narrow with `'taskId' in eventData`.
|
|
89
|
+
*/
|
|
90
|
+
export const WebhookCommentSchema = z.union([WebhookItemCommentSchema, WebhookProjectCommentSchema]);
|
|
91
|
+
export const CommentAddedPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.literal(CommentWebhookEventEnum.Added), eventData: WebhookCommentSchema }));
|
|
92
|
+
export const CommentUpdatedPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.literal(CommentWebhookEventEnum.Updated), eventData: WebhookCommentSchema }));
|
|
93
|
+
export const CommentDeletedPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.literal(CommentWebhookEventEnum.Deleted), eventData: WebhookCommentSchema }));
|
|
@@ -0,0 +1,51 @@
|
|
|
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 { LabelSchema } from '../labels/types.js';
|
|
14
|
+
import { BaseWebhookEnvelopeShape } from './envelope.js';
|
|
15
|
+
/**
|
|
16
|
+
* Enum-like object of label (`label:*`) webhook event names. Single source
|
|
17
|
+
* of truth for the discriminator values — `LABEL_WEBHOOK_EVENTS` and every
|
|
18
|
+
* `z.literal()` below derive from it.
|
|
19
|
+
*/
|
|
20
|
+
export const LabelWebhookEventEnum = {
|
|
21
|
+
Added: 'label:added',
|
|
22
|
+
Updated: 'label:updated',
|
|
23
|
+
Deleted: 'label:deleted',
|
|
24
|
+
};
|
|
25
|
+
/** Webhook events that carry a label as their `eventData`. */
|
|
26
|
+
export const LABEL_WEBHOOK_EVENTS = [
|
|
27
|
+
LabelWebhookEventEnum.Added,
|
|
28
|
+
LabelWebhookEventEnum.Updated,
|
|
29
|
+
LabelWebhookEventEnum.Deleted,
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* A label as delivered in a webhook payload.
|
|
33
|
+
*
|
|
34
|
+
* Webhook label payloads differ from the REST label shape in two ways:
|
|
35
|
+
* they use `item_order` (not `order`) and always include `is_deleted`. This
|
|
36
|
+
* schema reuses {@link LabelSchema}, swapping `order` for `itemOrder` on
|
|
37
|
+
* the input side and renaming it back after validation, and surfaces
|
|
38
|
+
* `isDeleted` for downstream filtering.
|
|
39
|
+
*/
|
|
40
|
+
export const WebhookLabelSchema = LabelSchema.omit({ order: true })
|
|
41
|
+
.extend({
|
|
42
|
+
itemOrder: z.number().int().nullable(),
|
|
43
|
+
isDeleted: z.boolean(),
|
|
44
|
+
})
|
|
45
|
+
.transform((_a) => {
|
|
46
|
+
var { itemOrder } = _a, rest = __rest(_a, ["itemOrder"]);
|
|
47
|
+
return (Object.assign(Object.assign({}, rest), { order: itemOrder }));
|
|
48
|
+
});
|
|
49
|
+
export const LabelAddedPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.literal(LabelWebhookEventEnum.Added), eventData: WebhookLabelSchema }));
|
|
50
|
+
export const LabelUpdatedPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.literal(LabelWebhookEventEnum.Updated), eventData: WebhookLabelSchema }));
|
|
51
|
+
export const LabelDeletedPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.literal(LabelWebhookEventEnum.Deleted), eventData: WebhookLabelSchema }));
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { WEBHOOK_EVENTS, WebhookEventSchema } from '../apps/types.js';
|
|
3
|
+
import { COMMENT_WEBHOOK_EVENTS, CommentAddedPayloadSchema, CommentDeletedPayloadSchema, CommentUpdatedPayloadSchema, } from './comments.js';
|
|
3
4
|
import { BaseWebhookEnvelopeShape } from './envelope.js';
|
|
5
|
+
import { LABEL_WEBHOOK_EVENTS, LabelAddedPayloadSchema, LabelDeletedPayloadSchema, LabelUpdatedPayloadSchema, } from './labels.js';
|
|
4
6
|
import { TASK_WEBHOOK_EVENTS, TaskAddedPayloadSchema, TaskCompletedPayloadSchema, TaskDeletedPayloadSchema, TaskUncompletedPayloadSchema, TaskUpdatedPayloadSchema, } from './tasks.js';
|
|
5
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Events whose payloads have been narrowed per resource. Each phase adds to
|
|
9
|
+
* this list; {@link UntypedWebhookPayloadSchema} below is derived by taking
|
|
10
|
+
* every {@link WebhookEvent} not present here.
|
|
11
|
+
*/
|
|
12
|
+
const TYPED_WEBHOOK_EVENTS = [
|
|
13
|
+
...TASK_WEBHOOK_EVENTS,
|
|
14
|
+
...COMMENT_WEBHOOK_EVENTS,
|
|
15
|
+
...LABEL_WEBHOOK_EVENTS,
|
|
16
|
+
];
|
|
17
|
+
const untypedEventNames = WEBHOOK_EVENTS.filter((event) => !TYPED_WEBHOOK_EVENTS.includes(event));
|
|
6
18
|
/**
|
|
7
19
|
* A single branch covering every event whose payload has not yet been typed
|
|
8
20
|
* per resource. Follow-up PRs will replace slices of this with dedicated
|
|
9
|
-
* variants (
|
|
21
|
+
* variants (projects, sections, filters, reminders).
|
|
10
22
|
*/
|
|
11
23
|
export const UntypedWebhookPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.enum(untypedEventNames), eventData: z.unknown(), eventDataExtra: z.unknown().optional() }));
|
|
12
24
|
/**
|
|
@@ -16,9 +28,11 @@ export const UntypedWebhookPayloadSchema = z.object(Object.assign(Object.assign(
|
|
|
16
28
|
* (`item:added`) in the `event_name` field, so consumers using
|
|
17
29
|
* `parseWebhookPayload` receive camelCase fields directly.
|
|
18
30
|
*
|
|
19
|
-
* Typed today: `item:*` events carry a parsed {@link Task}
|
|
20
|
-
*
|
|
21
|
-
*
|
|
31
|
+
* Typed today: `item:*` events carry a parsed {@link Task}; `note:*` events
|
|
32
|
+
* carry a parsed comment (item-comment or project-comment); `label:*` events
|
|
33
|
+
* carry a parsed label. Other events still expose `eventData` as `unknown`;
|
|
34
|
+
* they will be narrowed in follow-up PRs (projects, sections, filters,
|
|
35
|
+
* reminders).
|
|
22
36
|
*/
|
|
23
37
|
export const WebhookPayloadSchema = z.discriminatedUnion('eventName', [
|
|
24
38
|
TaskAddedPayloadSchema,
|
|
@@ -26,6 +40,12 @@ export const WebhookPayloadSchema = z.discriminatedUnion('eventName', [
|
|
|
26
40
|
TaskCompletedPayloadSchema,
|
|
27
41
|
TaskUncompletedPayloadSchema,
|
|
28
42
|
TaskDeletedPayloadSchema,
|
|
43
|
+
CommentAddedPayloadSchema,
|
|
44
|
+
CommentUpdatedPayloadSchema,
|
|
45
|
+
CommentDeletedPayloadSchema,
|
|
46
|
+
LabelAddedPayloadSchema,
|
|
47
|
+
LabelUpdatedPayloadSchema,
|
|
48
|
+
LabelDeletedPayloadSchema,
|
|
29
49
|
UntypedWebhookPayloadSchema,
|
|
30
50
|
]);
|
|
31
51
|
export { WebhookEventSchema };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const CLIENT_SECRET = "test-client-secret";
|
|
2
|
+
/** HMAC-SHA256 signs a raw body with the test client secret (or an override). */
|
|
3
|
+
export declare function sign(body: string, secret?: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Snake_case task payload matching the v1 webhook wire shape.
|
|
6
|
+
*/
|
|
7
|
+
export declare function rawTask(overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* Wraps an event's `event_data` in the snake_case envelope the backend sends
|
|
10
|
+
* (initiator, version, timestamps, etc). `extra` passes extra top-level keys
|
|
11
|
+
* like `event_data_extra`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function envelope(event: string, eventData: unknown, extra?: Record<string, unknown>): Record<string, unknown>;
|