@omnicoreos/planka-mcp 0.2.0 → 0.3.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/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +26 -0
- package/.claude-plugin/plugin.json +45 -0
- package/.codex-plugin/mcp.json +16 -0
- package/.codex-plugin/plugin.json +27 -0
- package/.mcp.json +17 -0
- package/CHANGELOG.md +510 -0
- package/README.es.md +294 -55
- package/README.md +293 -55
- package/dist/cli/init.d.ts +101 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +481 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/client.d.ts +32 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +106 -32
- package/dist/client.js.map +1 -1
- package/dist/config/policy.d.ts +82 -0
- package/dist/config/policy.d.ts.map +1 -0
- package/dist/config/policy.js +226 -0
- package/dist/config/policy.js.map +1 -0
- package/dist/errors.d.ts +5 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +62 -5
- package/dist/errors.js.map +1 -1
- package/dist/identity.generated.d.ts +2 -1
- package/dist/identity.generated.d.ts.map +1 -1
- package/dist/identity.generated.js +2 -1
- package/dist/identity.generated.js.map +1 -1
- package/dist/index.js +85 -10
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +21 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +37 -0
- package/dist/instructions.js.map +1 -0
- package/dist/operations/actions.d.ts +654 -0
- package/dist/operations/actions.d.ts.map +1 -0
- package/dist/operations/actions.js +154 -0
- package/dist/operations/actions.js.map +1 -0
- package/dist/operations/archive.d.ts +28 -0
- package/dist/operations/archive.d.ts.map +1 -0
- package/dist/operations/archive.js +74 -0
- package/dist/operations/archive.js.map +1 -0
- package/dist/operations/attachments.d.ts +1 -1
- package/dist/operations/attachments.d.ts.map +1 -1
- package/dist/operations/attachments.js +3 -1
- package/dist/operations/attachments.js.map +1 -1
- package/dist/operations/board-id.d.ts +1 -1
- package/dist/operations/board-id.d.ts.map +1 -1
- package/dist/operations/board-id.js +13 -7
- package/dist/operations/board-id.js.map +1 -1
- package/dist/operations/boards.d.ts +96 -19
- package/dist/operations/boards.d.ts.map +1 -1
- package/dist/operations/boards.js +377 -93
- package/dist/operations/boards.js.map +1 -1
- package/dist/operations/card-brief.d.ts +91 -0
- package/dist/operations/card-brief.d.ts.map +1 -0
- package/dist/operations/card-brief.js +79 -0
- package/dist/operations/card-brief.js.map +1 -0
- package/dist/operations/cards.d.ts +34 -9
- package/dist/operations/cards.d.ts.map +1 -1
- package/dist/operations/cards.js +60 -14
- package/dist/operations/cards.js.map +1 -1
- package/dist/operations/comments.d.ts +61 -4
- package/dist/operations/comments.d.ts.map +1 -1
- package/dist/operations/comments.js +91 -8
- package/dist/operations/comments.js.map +1 -1
- package/dist/operations/duplicate.d.ts +16 -0
- package/dist/operations/duplicate.d.ts.map +1 -0
- package/dist/operations/duplicate.js +43 -0
- package/dist/operations/duplicate.js.map +1 -0
- package/dist/operations/labels.d.ts +1 -1
- package/dist/operations/labels.d.ts.map +1 -1
- package/dist/operations/labels.js +7 -4
- package/dist/operations/labels.js.map +1 -1
- package/dist/operations/lists.d.ts +63 -1
- package/dist/operations/lists.d.ts.map +1 -1
- package/dist/operations/lists.js +97 -2
- package/dist/operations/lists.js.map +1 -1
- package/dist/operations/members.d.ts +39 -0
- package/dist/operations/members.d.ts.map +1 -0
- package/dist/operations/members.js +107 -0
- package/dist/operations/members.js.map +1 -0
- package/dist/operations/projects.d.ts +16 -0
- package/dist/operations/projects.d.ts.map +1 -1
- package/dist/operations/projects.js +54 -9
- package/dist/operations/projects.js.map +1 -1
- package/dist/operations/tasks.d.ts +1 -1
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/tasks.js +5 -3
- package/dist/operations/tasks.js.map +1 -1
- package/dist/operations/users.d.ts +123 -0
- package/dist/operations/users.d.ts.map +1 -0
- package/dist/operations/users.js +180 -0
- package/dist/operations/users.js.map +1 -0
- package/dist/operations/verify.d.ts +84 -0
- package/dist/operations/verify.d.ts.map +1 -0
- package/dist/operations/verify.js +124 -0
- package/dist/operations/verify.js.map +1 -0
- package/dist/prompts.d.ts +48 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +155 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +38 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +127 -0
- package/dist/resources.js.map +1 -0
- package/dist/schemas/entities.d.ts +115 -24
- package/dist/schemas/entities.d.ts.map +1 -1
- package/dist/schemas/entities.js +48 -0
- package/dist/schemas/entities.js.map +1 -1
- package/dist/schemas/requests.d.ts +121 -46
- package/dist/schemas/requests.d.ts.map +1 -1
- package/dist/schemas/requests.js +57 -12
- package/dist/schemas/requests.js.map +1 -1
- package/dist/schemas/responses.d.ts +541 -186
- package/dist/schemas/responses.d.ts.map +1 -1
- package/dist/schemas/responses.js +13 -2
- package/dist/schemas/responses.js.map +1 -1
- package/dist/tools/activity.d.ts +150 -0
- package/dist/tools/activity.d.ts.map +1 -0
- package/dist/tools/activity.js +198 -0
- package/dist/tools/activity.js.map +1 -0
- package/dist/tools/annotations.d.ts +52 -0
- package/dist/tools/annotations.d.ts.map +1 -0
- package/dist/tools/annotations.js +214 -0
- package/dist/tools/annotations.js.map +1 -0
- package/dist/tools/attachments.d.ts +28 -4
- package/dist/tools/attachments.d.ts.map +1 -1
- package/dist/tools/attachments.js +53 -34
- package/dist/tools/attachments.js.map +1 -1
- package/dist/tools/card-ops.d.ts +232 -0
- package/dist/tools/card-ops.d.ts.map +1 -0
- package/dist/tools/card-ops.js +333 -0
- package/dist/tools/card-ops.js.map +1 -0
- package/dist/tools/cards.d.ts +90 -8
- package/dist/tools/cards.d.ts.map +1 -1
- package/dist/tools/cards.js +411 -128
- package/dist/tools/cards.js.map +1 -1
- package/dist/tools/comments.d.ts +226 -22
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +163 -134
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/dispatch.d.ts +47 -0
- package/dist/tools/dispatch.d.ts.map +1 -0
- package/dist/tools/dispatch.js +63 -0
- package/dist/tools/dispatch.js.map +1 -0
- package/dist/tools/guard.d.ts +9 -0
- package/dist/tools/guard.d.ts.map +1 -0
- package/dist/tools/guard.js +20 -0
- package/dist/tools/guard.js.map +1 -0
- package/dist/tools/index.d.ts +748 -450
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +136 -17
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/labels.d.ts +213 -18
- package/dist/tools/labels.d.ts.map +1 -1
- package/dist/tools/labels.js +218 -203
- package/dist/tools/labels.js.map +1 -1
- package/dist/tools/lists.d.ts +222 -15
- package/dist/tools/lists.d.ts.map +1 -1
- package/dist/tools/lists.js +175 -156
- package/dist/tools/lists.js.map +1 -1
- package/dist/tools/members.d.ts +128 -0
- package/dist/tools/members.d.ts.map +1 -0
- package/dist/tools/members.js +150 -0
- package/dist/tools/members.js.map +1 -0
- package/dist/tools/navigation.d.ts +22 -2
- package/dist/tools/navigation.d.ts.map +1 -1
- package/dist/tools/navigation.js +60 -15
- package/dist/tools/navigation.js.map +1 -1
- package/dist/tools/queries.d.ts +196 -166
- package/dist/tools/queries.d.ts.map +1 -1
- package/dist/tools/queries.js +125 -155
- package/dist/tools/queries.js.map +1 -1
- package/dist/tools/tasks.d.ts +26 -6
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +110 -55
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/users.d.ts +130 -0
- package/dist/tools/users.d.ts.map +1 -0
- package/dist/tools/users.js +165 -0
- package/dist/tools/users.js.map +1 -0
- package/docs/planka-2x-gotchas.md +121 -5
- package/docs/tools.md +771 -187
- package/docs/troubleshooting.md +137 -5
- package/hooks/hooks.json +15 -0
- package/hooks/preflight.mjs +100 -0
- package/package.json +6 -1
- package/scripts/setup.sh +8 -26
- package/scripts/sync-identity.mjs +55 -1
- package/server.json +87 -6
- package/tests/smoke/planka-smoke.mjs +512 -72
- package/workflow/skills/planka-close-card/SKILL.md +18 -5
- package/workflow/skills/planka-orchestrator/SKILL.md +36 -7
|
@@ -30,24 +30,107 @@ export async function updateComment(input) {
|
|
|
30
30
|
*/
|
|
31
31
|
export async function deleteComment(input) {
|
|
32
32
|
const { commentId } = parseInput(CommentIdSchema, input);
|
|
33
|
-
await plankaClient.delete(`/api/comments/${commentId}`);
|
|
33
|
+
const body = await plankaClient.delete(`/api/comments/${commentId}`);
|
|
34
|
+
const parsed = CommentResponse.safeParse(body);
|
|
35
|
+
return parsed.success ? parsed.data.item : null;
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
38
|
+
* Planka 2.x serves comments 50 at a time and IGNORES ?limit / ?page. The only
|
|
39
|
+
* cursor is `?beforeId=<id>`, and the order is id DESC (newest first).
|
|
40
|
+
* Measured against the live instance — see docs/planka-2x-gotchas.md.
|
|
39
41
|
*/
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
export const COMMENTS_PAGE_SIZE = 50;
|
|
43
|
+
/** Ceiling for `all: true`: 10 pages = 500 comments, then `truncated`. */
|
|
44
|
+
export const COMMENTS_MAX_PAGES = 10;
|
|
45
|
+
/**
|
|
46
|
+
* One page of a card's comments (max 50), newest first.
|
|
47
|
+
* `beforeId` walks backwards in time: it returns comments OLDER than that id.
|
|
48
|
+
*/
|
|
49
|
+
export async function getCommentsPage(input) {
|
|
50
|
+
const { cardId } = parseInput(CardIdSchema, { cardId: input.cardId });
|
|
51
|
+
const query = input.beforeId
|
|
52
|
+
? `?beforeId=${encodeURIComponent(input.beforeId)}`
|
|
53
|
+
: "";
|
|
54
|
+
const response = await plankaClient.get(`/api/cards/${cardId}/comments${query}`);
|
|
43
55
|
const parsed = CommentsResponse.parse(response);
|
|
44
56
|
const included = CommentsIncludedSchema.parse(response.included || {});
|
|
45
57
|
const users = included.users || [];
|
|
46
|
-
|
|
58
|
+
const comments = parsed.items
|
|
47
59
|
.map((c) => ({
|
|
48
60
|
...c,
|
|
49
61
|
authorName: users.find((u) => u.id === c.userId)?.name,
|
|
50
62
|
}))
|
|
51
63
|
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
64
|
+
return {
|
|
65
|
+
comments,
|
|
66
|
+
full: parsed.items.length >= COMMENTS_PAGE_SIZE,
|
|
67
|
+
nextBeforeId: comments[comments.length - 1]?.id,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The FIRST page of a card's comments, newest first (max 50).
|
|
72
|
+
*
|
|
73
|
+
* Kept as the cheap read used by write verification: a comment that was just
|
|
74
|
+
* created or edited is on page 1 by definition. Anything that has to be
|
|
75
|
+
* complete — or honest about not being — goes through `getComments`.
|
|
76
|
+
*/
|
|
77
|
+
export async function getCommentsForCard(input) {
|
|
78
|
+
const { cardId } = parseInput(CardIdSchema, input);
|
|
79
|
+
const page = await getCommentsPage({ cardId });
|
|
80
|
+
return page.comments;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Read a card's comments with honest paging.
|
|
84
|
+
*
|
|
85
|
+
* - `limit` how many to return (1..50 per server page; default up to the caller)
|
|
86
|
+
* - `beforeId` cursor from a previous call — returns comments OLDER than it
|
|
87
|
+
* - `all` walk every page up to COMMENTS_MAX_PAGES, then say `truncated`
|
|
88
|
+
* - `order` "asc" (oldest first, the reading order) or "desc" (newest first)
|
|
89
|
+
*
|
|
90
|
+
* `hasMore` is never guessed: it is true only when Planka handed back a full
|
|
91
|
+
* page, or when a page carried more comments than the caller asked for.
|
|
92
|
+
*/
|
|
93
|
+
export async function getComments(input) {
|
|
94
|
+
const order = input.order ?? "asc";
|
|
95
|
+
if (input.all) {
|
|
96
|
+
const maxPages = Math.max(1, input.maxPages ?? COMMENTS_MAX_PAGES);
|
|
97
|
+
const collected = [];
|
|
98
|
+
let beforeId = input.beforeId;
|
|
99
|
+
let pagesFetched = 0;
|
|
100
|
+
let full = false;
|
|
101
|
+
while (pagesFetched < maxPages) {
|
|
102
|
+
const page = await getCommentsPage({ cardId: input.cardId, beforeId });
|
|
103
|
+
pagesFetched += 1;
|
|
104
|
+
collected.push(...page.comments);
|
|
105
|
+
full = page.full;
|
|
106
|
+
beforeId = page.nextBeforeId;
|
|
107
|
+
if (!page.full || !beforeId)
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
// Ran out of pages while Planka still had more to give.
|
|
111
|
+
const truncated = full && pagesFetched >= maxPages;
|
|
112
|
+
return {
|
|
113
|
+
comments: order === "asc" ? [...collected].reverse() : collected,
|
|
114
|
+
hasMore: truncated,
|
|
115
|
+
...(truncated && beforeId ? { nextBeforeId: beforeId } : {}),
|
|
116
|
+
truncated,
|
|
117
|
+
pagesFetched,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const page = await getCommentsPage({
|
|
121
|
+
cardId: input.cardId,
|
|
122
|
+
beforeId: input.beforeId,
|
|
123
|
+
});
|
|
124
|
+
const limit = Math.max(1, Math.min(input.limit ?? COMMENTS_PAGE_SIZE, COMMENTS_PAGE_SIZE));
|
|
125
|
+
const slice = page.comments.slice(0, limit);
|
|
126
|
+
const hasMore = page.comments.length > slice.length || page.full;
|
|
127
|
+
const nextBeforeId = slice[slice.length - 1]?.id;
|
|
128
|
+
return {
|
|
129
|
+
comments: order === "asc" ? [...slice].reverse() : slice,
|
|
130
|
+
hasMore,
|
|
131
|
+
...(hasMore && nextBeforeId ? { nextBeforeId } : {}),
|
|
132
|
+
truncated: false,
|
|
133
|
+
pagesFetched: 1,
|
|
134
|
+
};
|
|
52
135
|
}
|
|
53
136
|
//# sourceMappingURL=comments.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../../src/operations/comments.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EACL,YAAY,EAEZ,eAAe,EAEf,mBAAmB,EAEnB,yBAAyB,GAE1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAUpG;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAyB;IAC3D,MAAM,SAAS,GAAG,UAAU,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;IAEzD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CACtC,cAAc,SAAS,CAAC,MAAM,WAAW,EACzC;QACE,IAAI,EAAE,SAAS,CAAC,IAAI;KACrB,CACF,CAAC;IAEF,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAA+B;IAE/B,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,GAAG,UAAU,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;IAE/E,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,KAAK,CACvC,iBAAiB,SAAS,EAAE,EAC5B,OAAO,CACR,CAAC;IAEF,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAqB;IACvD,MAAM,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IACzD,MAAM,YAAY,CAAC,MAAM,CAAC,iBAAiB,SAAS,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../../src/operations/comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate a card. `position` defaults to Planka's usual end-of-column value.
|
|
3
|
+
*/
|
|
4
|
+
export declare function duplicateCard(input: {
|
|
5
|
+
cardId: string;
|
|
6
|
+
listId?: string;
|
|
7
|
+
boardId?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
position?: number;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
listId: string;
|
|
14
|
+
boardId: string;
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=duplicate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate.d.ts","sourceRoot":"","sources":["../../src/operations/duplicate.ts"],"names":[],"mappings":"AAqBA;;GAEG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBzE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card duplication.
|
|
3
|
+
*
|
|
4
|
+
* `POST /api/cards/:id/duplicate` is a real Planka 2.x endpoint and it copies
|
|
5
|
+
* more than the card row: verified live on 2.2.1, the response's `included`
|
|
6
|
+
* came back with the card's memberships, labels, task lists, tasks and custom
|
|
7
|
+
* field values. Attachments are NOT re-uploaded. The copy lands in the SAME
|
|
8
|
+
* list unless `listId` (and optionally `boardId`) say otherwise, and Planka
|
|
9
|
+
* appends its own suffix to the name unless `name` is given.
|
|
10
|
+
*
|
|
11
|
+
* The counts reported to the caller come from re-reading the new card, not
|
|
12
|
+
* from this response: the point of the tool is to say what the copy actually
|
|
13
|
+
* carries.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { plankaClient } from "../client.js";
|
|
17
|
+
import { assertBoardAllowed, assertListWritable } from "../config/policy.js";
|
|
18
|
+
import { CardBriefSchema } from "./card-brief.js";
|
|
19
|
+
const DuplicateResponse = z.object({ item: CardBriefSchema });
|
|
20
|
+
/**
|
|
21
|
+
* Duplicate a card. `position` defaults to Planka's usual end-of-column value.
|
|
22
|
+
*/
|
|
23
|
+
export async function duplicateCard(input) {
|
|
24
|
+
assertListWritable(input.listId, undefined, "duplicate cards into");
|
|
25
|
+
if (input.boardId)
|
|
26
|
+
assertBoardAllowed(input.boardId);
|
|
27
|
+
const body = { position: input.position ?? 65536 };
|
|
28
|
+
if (input.listId)
|
|
29
|
+
body.listId = input.listId;
|
|
30
|
+
if (input.boardId)
|
|
31
|
+
body.boardId = input.boardId;
|
|
32
|
+
if (input.name !== undefined)
|
|
33
|
+
body.name = input.name;
|
|
34
|
+
const response = await plankaClient.post(`/api/cards/${input.cardId}/duplicate`, body);
|
|
35
|
+
const item = DuplicateResponse.parse(response).item;
|
|
36
|
+
return {
|
|
37
|
+
id: item.id,
|
|
38
|
+
name: item.name,
|
|
39
|
+
listId: item.listId,
|
|
40
|
+
boardId: item.boardId,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=duplicate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate.js","sourceRoot":"","sources":["../../src/operations/duplicate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;AAE9D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAMnC;IACC,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,sBAAsB,CAAC,CAAC;IACpE,IAAI,KAAK,CAAC,OAAO;QAAE,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAErD,MAAM,IAAI,GAA4B,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,EAAE,CAAC;IAC5E,IAAI,KAAK,CAAC,MAAM;QAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC7C,IAAI,KAAK,CAAC,OAAO;QAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAChD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CACtC,cAAc,KAAK,CAAC,MAAM,YAAY,EACtC,IAAI,CACL,CAAC;IACF,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;IAEpD,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -11,7 +11,7 @@ export declare function updateLabel(input: UpdateLabelParamsInput): Promise<Labe
|
|
|
11
11
|
/**
|
|
12
12
|
* Delete a label.
|
|
13
13
|
*/
|
|
14
|
-
export declare function deleteLabel(input: LabelIdInput): Promise<
|
|
14
|
+
export declare function deleteLabel(input: LabelIdInput): Promise<Label | null>;
|
|
15
15
|
/**
|
|
16
16
|
* Add a label to a card.
|
|
17
17
|
* Uses PLANKA 2.0 /card-labels endpoint.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/operations/labels.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAEL,mBAAmB,EAEnB,gBAAgB,EAEhB,YAAY,EAEZ,wBAAwB,EAExB,kBAAkB,EAElB,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAIhC;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAgBzE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,KAAK,CAAC,CAUhB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/operations/labels.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAEL,mBAAmB,EAEnB,gBAAgB,EAEhB,YAAY,EAEZ,wBAAwB,EAExB,kBAAkB,EAElB,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAIhC;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAgBzE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,KAAK,CAAC,CAUhB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAK5E;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CAYnF;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,wCAAwC;IACxC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gDAAgD;IAChD,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,4EAA4E;IAC5E,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,2DAA2D;IAC3D,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAyF9B"}
|
|
@@ -6,7 +6,7 @@ import { withBoardId } from "./board-id.js";
|
|
|
6
6
|
import { parseInput } from "../errors.js";
|
|
7
7
|
import { AddLabelToCardSchema, CreateLabelSchema, LabelIdSchema, RemoveLabelFromCardSchema, SetCardLabelsSchema, UpdateLabelParamsSchema, } from "../schemas/requests.js";
|
|
8
8
|
import { LabelResponse, CardLabelResponse } from "../schemas/responses.js";
|
|
9
|
-
import {
|
|
9
|
+
import { getCardLight } from "./cards.js";
|
|
10
10
|
/**
|
|
11
11
|
* Create a new label on a board.
|
|
12
12
|
*/
|
|
@@ -36,7 +36,9 @@ export async function updateLabel(input) {
|
|
|
36
36
|
*/
|
|
37
37
|
export async function deleteLabel(input) {
|
|
38
38
|
const { labelId } = parseInput(LabelIdSchema, input);
|
|
39
|
-
await plankaClient.delete(`/api/labels/${labelId}`);
|
|
39
|
+
const body = await plankaClient.delete(`/api/labels/${labelId}`);
|
|
40
|
+
const parsed = LabelResponse.safeParse(body);
|
|
41
|
+
return parsed.success ? parsed.data.item : null;
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
44
|
* Add a label to a card.
|
|
@@ -72,7 +74,8 @@ export async function setCardLabels(input) {
|
|
|
72
74
|
const { cardId, addLabelIds, removeLabelIds } = parseInput(SetCardLabelsSchema, input);
|
|
73
75
|
const toAdd = addLabelIds ?? [];
|
|
74
76
|
const toRemove = removeLabelIds ?? [];
|
|
75
|
-
|
|
77
|
+
// Light read: comparing label ids never needed the card's comments.
|
|
78
|
+
const before = await getCardLight({ cardId });
|
|
76
79
|
const presentBefore = new Set(before.cardLabels.map((cl) => cl.labelId));
|
|
77
80
|
const result = {
|
|
78
81
|
added: [],
|
|
@@ -117,7 +120,7 @@ export async function setCardLabels(input) {
|
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
// Verify against the card itself: this is what makes the report trustworthy.
|
|
120
|
-
const after = await
|
|
123
|
+
const after = await getCardLight({ cardId });
|
|
121
124
|
const presentAfter = new Set(after.cardLabels.map((cl) => cl.labelId));
|
|
122
125
|
result.finalLabelIds = [...presentAfter];
|
|
123
126
|
result.added = toAdd.filter((id) => presentAfter.has(id) && !presentBefore.has(id));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../src/operations/labels.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EACL,oBAAoB,EAEpB,iBAAiB,EAEjB,aAAa,EAEb,yBAAyB,EAEzB,mBAAmB,EAEnB,uBAAuB,GAExB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../src/operations/labels.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,5 +11,67 @@ export declare function updateList(input: UpdateListParamsInput): Promise<List>;
|
|
|
11
11
|
/**
|
|
12
12
|
* Delete a list.
|
|
13
13
|
*/
|
|
14
|
-
export declare function deleteList(input: ListIdInput): Promise<
|
|
14
|
+
export declare function deleteList(input: ListIdInput): Promise<List | null>;
|
|
15
|
+
/** What a column holds right now, and what kind of column it is. */
|
|
16
|
+
export interface ListState {
|
|
17
|
+
id: string;
|
|
18
|
+
/** "active" | "closed" | "archive" | "trash", when Planka will say. */
|
|
19
|
+
type: string | null;
|
|
20
|
+
boardId: string | null;
|
|
21
|
+
cardIds: string[];
|
|
22
|
+
/** Card id -> its position, so a per-card move can preserve the order. */
|
|
23
|
+
positions: Map<string, number | null>;
|
|
24
|
+
count: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Reads a column: its type, its board and every card in it.
|
|
28
|
+
*
|
|
29
|
+
* `GET /api/lists/:id` returns the WHOLE column in `included.cards` — no 50-item
|
|
30
|
+
* page, unlike `GET /api/lists/:id/cards` — which is what makes it usable both
|
|
31
|
+
* as a counter and as the source of a per-card move.
|
|
32
|
+
*
|
|
33
|
+
* Returns `null` for a column Planka refuses to show: the hidden `archive` and
|
|
34
|
+
* `trash` lists answer 404 here. That is not an error, it is information.
|
|
35
|
+
*/
|
|
36
|
+
export declare function readListState(listId: string): Promise<ListState | null>;
|
|
37
|
+
/** How many cards a column holds. `null` when the column cannot be read. */
|
|
38
|
+
export declare function countListCards(listId: string): Promise<number | null>;
|
|
39
|
+
/**
|
|
40
|
+
* Planka's own bulk move — and the ONLY thing it can do.
|
|
41
|
+
*
|
|
42
|
+
* `POST /api/lists/:id/move-cards` reads like a general "move every card from
|
|
43
|
+
* A to B", and it is not: `server/api/controllers/lists/move-cards.js` in 2.2.1
|
|
44
|
+
* refuses anything but archiving a whole closed column —
|
|
45
|
+
*
|
|
46
|
+
* if (list.type !== List.Types.CLOSED) throw NOT_ENOUGH_RIGHTS; // 403
|
|
47
|
+
* if (nextList.type !== List.Types.ARCHIVE) throw LIST_NOT_FOUND; // 404
|
|
48
|
+
*
|
|
49
|
+
* so an active -> active call comes back 403 "Not enough rights", which reads
|
|
50
|
+
* like a permission problem and is not one. Callers that need the general move
|
|
51
|
+
* fall back to `moveCardsOneByOne`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function bulkMoveClosedListToArchive(input: {
|
|
54
|
+
fromListId: string;
|
|
55
|
+
toListId: string;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* The general move: one PATCH per card.
|
|
59
|
+
*
|
|
60
|
+
* Each card keeps the position it had, so the relative order survives. A card
|
|
61
|
+
* whose position is null (it came out of the archive) gets one, because Planka
|
|
62
|
+
* rejects a move without it.
|
|
63
|
+
*
|
|
64
|
+
* Returns the cards that could not be moved, with the reason — a partial move
|
|
65
|
+
* has to be visible, not silently reported as a smaller success.
|
|
66
|
+
*/
|
|
67
|
+
export declare function moveCardsOneByOne(input: {
|
|
68
|
+
cards: Array<{
|
|
69
|
+
id: string;
|
|
70
|
+
position: number | null;
|
|
71
|
+
}>;
|
|
72
|
+
toListId: string;
|
|
73
|
+
}): Promise<Array<{
|
|
74
|
+
cardId: string;
|
|
75
|
+
reason: string;
|
|
76
|
+
}>>;
|
|
15
77
|
//# sourceMappingURL=lists.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lists.d.ts","sourceRoot":"","sources":["../../src/operations/lists.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lists.d.ts","sourceRoot":"","sources":["../../src/operations/lists.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAE9C,OAAO,EAEL,eAAe,EAEf,WAAW,EAEX,qBAAqB,EACtB,MAAM,wBAAwB,CAAC;AAEhC;;GAEG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBtE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAMzE;AAED,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0EAA0E;IAC1E,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA2B7E;AAED,4EAA4E;AAC5E,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,2BAA2B,CAAC,KAAK,EAAE;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAC7C,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAkBrD"}
|
package/dist/operations/lists.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { plankaClient } from "../client.js";
|
|
5
5
|
import { withBoardId } from "./board-id.js";
|
|
6
|
-
import { parseInput } from "../errors.js";
|
|
6
|
+
import { PlankaError, parseInput } from "../errors.js";
|
|
7
|
+
import { assertListWritable } from "../config/policy.js";
|
|
7
8
|
import { ListResponse } from "../schemas/responses.js";
|
|
8
9
|
import { CreateListSchema, ListIdSchema, UpdateListParamsSchema, } from "../schemas/requests.js";
|
|
9
10
|
/**
|
|
@@ -26,6 +27,7 @@ export async function createList(input) {
|
|
|
26
27
|
*/
|
|
27
28
|
export async function updateList(input) {
|
|
28
29
|
const { listId, ...updates } = parseInput(UpdateListParamsSchema, input);
|
|
30
|
+
assertListWritable(listId, undefined, "edit");
|
|
29
31
|
const response = await plankaClient.patch(`/api/lists/${listId}`, updates);
|
|
30
32
|
const parsed = ListResponse.parse(response);
|
|
31
33
|
return parsed.item;
|
|
@@ -35,6 +37,99 @@ export async function updateList(input) {
|
|
|
35
37
|
*/
|
|
36
38
|
export async function deleteList(input) {
|
|
37
39
|
const { listId } = parseInput(ListIdSchema, input);
|
|
38
|
-
|
|
40
|
+
assertListWritable(listId, undefined, "delete");
|
|
41
|
+
const body = await plankaClient.delete(`/api/lists/${listId}`);
|
|
42
|
+
const parsed = ListResponse.safeParse(body);
|
|
43
|
+
return parsed.success ? parsed.data.item : null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Reads a column: its type, its board and every card in it.
|
|
47
|
+
*
|
|
48
|
+
* `GET /api/lists/:id` returns the WHOLE column in `included.cards` — no 50-item
|
|
49
|
+
* page, unlike `GET /api/lists/:id/cards` — which is what makes it usable both
|
|
50
|
+
* as a counter and as the source of a per-card move.
|
|
51
|
+
*
|
|
52
|
+
* Returns `null` for a column Planka refuses to show: the hidden `archive` and
|
|
53
|
+
* `trash` lists answer 404 here. That is not an error, it is information.
|
|
54
|
+
*/
|
|
55
|
+
export async function readListState(listId) {
|
|
56
|
+
let response;
|
|
57
|
+
try {
|
|
58
|
+
response = await plankaClient.get(`/api/lists/${listId}`);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (error instanceof PlankaError && error.status === 404)
|
|
62
|
+
return null;
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
const cards = response?.included?.cards ?? [];
|
|
66
|
+
const positions = new Map();
|
|
67
|
+
for (const card of cards) {
|
|
68
|
+
if (card?.id)
|
|
69
|
+
positions.set(card.id, card.position ?? null);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
id: listId,
|
|
73
|
+
type: response?.item?.type ?? null,
|
|
74
|
+
boardId: response?.item?.boardId ?? null,
|
|
75
|
+
cardIds: [...positions.keys()],
|
|
76
|
+
positions,
|
|
77
|
+
count: positions.size,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** How many cards a column holds. `null` when the column cannot be read. */
|
|
81
|
+
export async function countListCards(listId) {
|
|
82
|
+
const state = await readListState(listId);
|
|
83
|
+
return state ? state.count : null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Planka's own bulk move — and the ONLY thing it can do.
|
|
87
|
+
*
|
|
88
|
+
* `POST /api/lists/:id/move-cards` reads like a general "move every card from
|
|
89
|
+
* A to B", and it is not: `server/api/controllers/lists/move-cards.js` in 2.2.1
|
|
90
|
+
* refuses anything but archiving a whole closed column —
|
|
91
|
+
*
|
|
92
|
+
* if (list.type !== List.Types.CLOSED) throw NOT_ENOUGH_RIGHTS; // 403
|
|
93
|
+
* if (nextList.type !== List.Types.ARCHIVE) throw LIST_NOT_FOUND; // 404
|
|
94
|
+
*
|
|
95
|
+
* so an active -> active call comes back 403 "Not enough rights", which reads
|
|
96
|
+
* like a permission problem and is not one. Callers that need the general move
|
|
97
|
+
* fall back to `moveCardsOneByOne`.
|
|
98
|
+
*/
|
|
99
|
+
export async function bulkMoveClosedListToArchive(input) {
|
|
100
|
+
assertListWritable(input.toListId, undefined, "move cards into");
|
|
101
|
+
assertListWritable(input.fromListId, undefined, "move cards out of");
|
|
102
|
+
await plankaClient.post(`/api/lists/${input.fromListId}/move-cards`, {
|
|
103
|
+
listId: input.toListId,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The general move: one PATCH per card.
|
|
108
|
+
*
|
|
109
|
+
* Each card keeps the position it had, so the relative order survives. A card
|
|
110
|
+
* whose position is null (it came out of the archive) gets one, because Planka
|
|
111
|
+
* rejects a move without it.
|
|
112
|
+
*
|
|
113
|
+
* Returns the cards that could not be moved, with the reason — a partial move
|
|
114
|
+
* has to be visible, not silently reported as a smaller success.
|
|
115
|
+
*/
|
|
116
|
+
export async function moveCardsOneByOne(input) {
|
|
117
|
+
assertListWritable(input.toListId, undefined, "move cards into");
|
|
118
|
+
const failed = [];
|
|
119
|
+
for (const card of input.cards) {
|
|
120
|
+
try {
|
|
121
|
+
await plankaClient.patch(`/api/cards/${card.id}`, {
|
|
122
|
+
listId: input.toListId,
|
|
123
|
+
position: card.position ?? 65536,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
failed.push({
|
|
128
|
+
cardId: card.id,
|
|
129
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return failed;
|
|
39
134
|
}
|
|
40
135
|
//# sourceMappingURL=lists.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lists.js","sourceRoot":"","sources":["../../src/operations/lists.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"lists.js","sourceRoot":"","sources":["../../src/operations/lists.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EACL,gBAAgB,EAEhB,YAAY,EAEZ,sBAAsB,GAEvB,MAAM,wBAAwB,CAAC;AAEhC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAsB;IACrD,MAAM,SAAS,GAAG,UAAU,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;IAEtD,OAAO,WAAW,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACtD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CACtC,eAAe,OAAO,QAAQ,EAC9B;YACE,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,+CAA+C;SACtE,CACF,CAAC;QAEF,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC5C,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAA4B;IAE5B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,GAAG,UAAU,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;IACzE,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,KAAK,CACvC,cAAc,MAAM,EAAE,EACtB,OAAO,CACR,CAAC;IAEF,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAkB;IACjD,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IACnD,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC;AAcD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAc;IAChD,IAAI,QAGH,CAAC;IAEF,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QACtE,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC;IAC9C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IACnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,EAAE,EAAE;YAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO;QACL,EAAE,EAAE,MAAM;QACV,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI;QAClC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI;QACxC,OAAO,EAAE,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;QAC9B,SAAS;QACT,KAAK,EAAE,SAAS,CAAC,IAAI;KACtB,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc;IACjD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,KAGjD;IACC,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;IACjE,kBAAkB,CAAC,KAAK,CAAC,UAAU,EAAE,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAErE,MAAM,YAAY,CAAC,IAAI,CAAU,cAAc,KAAK,CAAC,UAAU,aAAa,EAAE;QAC5E,MAAM,EAAE,KAAK,CAAC,QAAQ;KACvB,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAGvC;IACC,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;IAEjE,MAAM,MAAM,GAA8C,EAAE,CAAC;IAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,YAAY,CAAC,KAAK,CAAU,cAAc,IAAI,CAAC,EAAE,EAAE,EAAE;gBACzD,MAAM,EAAE,KAAK,CAAC,QAAQ;gBACtB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;aACjC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC;gBACV,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC/D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Put a user on a card. */
|
|
2
|
+
export declare function addCardMember(input: {
|
|
3
|
+
cardId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
id: string;
|
|
7
|
+
userId: string;
|
|
8
|
+
}>;
|
|
9
|
+
/** Take a user off a card. */
|
|
10
|
+
export declare function removeCardMember(input: {
|
|
11
|
+
cardId: string;
|
|
12
|
+
userId: string;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
export interface SetCardMembersResult {
|
|
15
|
+
/** Users this call put on the card. */
|
|
16
|
+
added: string[];
|
|
17
|
+
/** Users asked for that were already there. */
|
|
18
|
+
alreadyPresent: string[];
|
|
19
|
+
/** Users this call took off the card. */
|
|
20
|
+
removed: string[];
|
|
21
|
+
/** Users asked to be removed that were not on the card. */
|
|
22
|
+
notPresent: string[];
|
|
23
|
+
/** Requests that did NOT reach the desired state. */
|
|
24
|
+
failed: Array<{
|
|
25
|
+
userId: string;
|
|
26
|
+
reason: string;
|
|
27
|
+
}>;
|
|
28
|
+
/** Every member of the card once the operation finished. */
|
|
29
|
+
finalMemberIds: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add and/or remove card members, then re-read to report what really landed.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setCardMembers(input: {
|
|
35
|
+
cardId: string;
|
|
36
|
+
add?: string[];
|
|
37
|
+
remove?: string[];
|
|
38
|
+
}): Promise<SetCardMembersResult>;
|
|
39
|
+
//# sourceMappingURL=members.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"members.d.ts","sourceRoot":"","sources":["../../src/operations/members.ts"],"names":[],"mappings":"AAuBA,4BAA4B;AAC5B,wBAAsB,aAAa,CAAC,KAAK,EAAE;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAM1C;AAED,8BAA8B;AAC9B,wBAAsB,gBAAgB,CAAC,KAAK,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AAED,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+CAA+C;IAC/C,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,yCAAyC;IACzC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,qDAAqD;IACrD,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,4DAA4D;IAC5D,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA+EhC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card members (assignees).
|
|
3
|
+
*
|
|
4
|
+
* The mirror image of `setCardLabels`, and for the same reason: a 200 on
|
|
5
|
+
* `POST /card-memberships` proves the request was accepted, not that the
|
|
6
|
+
* person is on the card. A userId that is not a member of the board answers
|
|
7
|
+
* 404 (Planka's "not found" for "not allowed"), and reporting the REQUESTED
|
|
8
|
+
* assignment would leave an agent believing a card is assigned to nobody.
|
|
9
|
+
*
|
|
10
|
+
* So this writes, re-reads `included.cardMemberships`, and reports the final
|
|
11
|
+
* membership set.
|
|
12
|
+
*
|
|
13
|
+
* The DELETE path is Planka's criteria syntax, `userId:<id>` — a literal colon
|
|
14
|
+
* in the URL, not the membership record's own id.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { plankaClient } from "../client.js";
|
|
18
|
+
import { getCardBrief } from "./card-brief.js";
|
|
19
|
+
const MembershipResponse = z.object({
|
|
20
|
+
item: z.object({ id: z.string(), cardId: z.string(), userId: z.string() }).passthrough(),
|
|
21
|
+
});
|
|
22
|
+
/** Put a user on a card. */
|
|
23
|
+
export async function addCardMember(input) {
|
|
24
|
+
const response = await plankaClient.post(`/api/cards/${input.cardId}/card-memberships`, { userId: input.userId });
|
|
25
|
+
return MembershipResponse.parse(response).item;
|
|
26
|
+
}
|
|
27
|
+
/** Take a user off a card. */
|
|
28
|
+
export async function removeCardMember(input) {
|
|
29
|
+
await plankaClient.delete(`/api/cards/${input.cardId}/card-memberships/userId:${input.userId}`);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add and/or remove card members, then re-read to report what really landed.
|
|
33
|
+
*/
|
|
34
|
+
export async function setCardMembers(input) {
|
|
35
|
+
const toAdd = [...new Set(input.add ?? [])];
|
|
36
|
+
const toRemove = [...new Set(input.remove ?? [])];
|
|
37
|
+
const before = await getCardBrief({ cardId: input.cardId });
|
|
38
|
+
const presentBefore = new Set(before.memberIds);
|
|
39
|
+
const result = {
|
|
40
|
+
added: [],
|
|
41
|
+
alreadyPresent: [],
|
|
42
|
+
removed: [],
|
|
43
|
+
notPresent: [],
|
|
44
|
+
failed: [],
|
|
45
|
+
finalMemberIds: [],
|
|
46
|
+
};
|
|
47
|
+
// Remove first, so one call can hand a card over from one person to another.
|
|
48
|
+
for (const userId of toRemove) {
|
|
49
|
+
if (!presentBefore.has(userId)) {
|
|
50
|
+
result.notPresent.push(userId);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
await removeCardMember({ cardId: input.cardId, userId });
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
result.failed.push({
|
|
58
|
+
userId,
|
|
59
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const userId of toAdd) {
|
|
64
|
+
if (presentBefore.has(userId)) {
|
|
65
|
+
result.alreadyPresent.push(userId);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
await addCardMember({ cardId: input.cardId, userId });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
73
|
+
// Already assigned is the desired state, not a failure.
|
|
74
|
+
if (message.toLowerCase().includes("already")) {
|
|
75
|
+
result.alreadyPresent.push(userId);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
result.failed.push({
|
|
79
|
+
userId,
|
|
80
|
+
reason: message +
|
|
81
|
+
(message.includes("404")
|
|
82
|
+
? " (a 404 here usually means the user is not a member of this board)"
|
|
83
|
+
: ""),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const after = await getCardBrief({ cardId: input.cardId });
|
|
88
|
+
const presentAfter = new Set(after.memberIds);
|
|
89
|
+
result.finalMemberIds = [...presentAfter];
|
|
90
|
+
result.added = toAdd.filter((id) => presentAfter.has(id) && !presentBefore.has(id));
|
|
91
|
+
result.removed = toRemove.filter((id) => !presentAfter.has(id) && presentBefore.has(id));
|
|
92
|
+
// Anything asked for that the re-read contradicts is a failure, even if the
|
|
93
|
+
// HTTP call came back 200.
|
|
94
|
+
const reported = new Set(result.failed.map((f) => f.userId));
|
|
95
|
+
for (const userId of toAdd) {
|
|
96
|
+
if (!presentAfter.has(userId) && !reported.has(userId)) {
|
|
97
|
+
result.failed.push({ userId, reason: "still not a member of the card after the write" });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const userId of toRemove) {
|
|
101
|
+
if (presentAfter.has(userId) && !reported.has(userId)) {
|
|
102
|
+
result.failed.push({ userId, reason: "still a member of the card after the write" });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=members.js.map
|