@evanston/plankton-core 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Evan Kim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @evanston/plankton-core
2
+
3
+ Provider-independent TypeScript client for Planka 2.0.0-rc.4. Requires Node.js 22+. Original code is MIT licensed. This initial version has only source/fixture validation against rc.4, not live production testing.
4
+
5
+ ```ts
6
+ import { PlankaClient, errorResult } from '@evanston/plankton-core';
7
+ const client = new PlankaClient({ url: 'https://planka.example', session: await yourVault.read() });
8
+ try {
9
+ const result = await client.execute('create_card', {
10
+ board: 'Product', list: 'Todo', name: 'Review onboarding',
11
+ });
12
+ console.log(result.item);
13
+ } catch (error) { console.error(errorResult(error)); }
14
+ ```
15
+
16
+ Session input is `{ accessToken, httpOnlyToken? }`; keep it in a credential vault. Core uses native fetch and Zod. It has no browser, MCP, keyring or AI-provider dependency.
17
+
18
+ `account()` verifies the current user. `execute(operation, input)` exposes projects, boards, lists, find_cards, read_card, create_card, edit_card, move_card, task_lists, create_task_list, edit_task_list, add_task and complete_task. Exported `operationSchemas` define each input. Board/card IDs and same-instance links work directly; names are scoped and ambiguous matches return choices. Card search is bounded and returns `truncated` when incomplete. Error results are safe to share; write uncertainty is never retried automatically. Moving/creating cards in trash is excluded.
@@ -0,0 +1,22 @@
1
+ import { type Operation, type OperationInput } from './schemas.js';
2
+ import { type ClientOptions } from './transport.js';
3
+ export declare class PlankaClient {
4
+ readonly url: string;
5
+ private readonly transport;
6
+ constructor(options: ClientOptions);
7
+ private item;
8
+ private board;
9
+ private card;
10
+ private writableList;
11
+ private link;
12
+ account(): Promise<{
13
+ id: string;
14
+ name: string | undefined;
15
+ username: unknown;
16
+ }>;
17
+ private search;
18
+ execute<K extends Operation>(operation: K, input: OperationInput<K>): Promise<Record<string, unknown>>;
19
+ private writeItem;
20
+ private executeCard;
21
+ private executeTask;
22
+ }
package/dist/client.js ADDED
@@ -0,0 +1,190 @@
1
+ import { PlanktonError } from './errors.js';
2
+ import { referenceId, resolveReference } from './references.js';
3
+ import { operationSchemas, } from './schemas.js';
4
+ import { PlankaTransport } from './transport.js';
5
+ export class PlankaClient {
6
+ url;
7
+ transport;
8
+ constructor(options) {
9
+ this.transport = new PlankaTransport(options);
10
+ this.url = this.transport.url;
11
+ }
12
+ item(data) {
13
+ if (!data.item) {
14
+ throw new PlanktonError('API', 'Planka response is missing the expected resource.');
15
+ }
16
+ return data.item;
17
+ }
18
+ async board(value) {
19
+ const id = referenceId(this.url, value, 'boards') ??
20
+ resolveReference(this.url, value, (await this.transport.request('projects')).included?.boards ?? [], 'boards').id;
21
+ return this.transport.request(`boards/${id}`);
22
+ }
23
+ async card(value, board) {
24
+ let id = referenceId(this.url, value, 'cards');
25
+ if (!id) {
26
+ if (!board) {
27
+ throw new PlanktonError('VALIDATION', 'Supply a board to resolve a card name, or use a card ID/link.');
28
+ }
29
+ const found = await this.search(board, value, 100);
30
+ if (found.truncated) {
31
+ throw new PlanktonError('VALIDATION', 'Card search is incomplete. Use a card ID/link to avoid resolving the wrong card.');
32
+ }
33
+ id = resolveReference(this.url, value, found.items, 'cards').id;
34
+ }
35
+ return this.transport.request(`cards/${id}`);
36
+ }
37
+ writableList(list) {
38
+ if (list.type === 'trash') {
39
+ throw new PlanktonError('VALIDATION', 'Moving or creating cards in trash is outside the initial scope.');
40
+ }
41
+ }
42
+ link(item, kind = 'cards') {
43
+ return { ...item, url: `${this.url}/${kind}/${item.id}` };
44
+ }
45
+ async account() {
46
+ const item = this.item(await this.transport.request('users/me'));
47
+ return { id: item.id, name: item.name, username: item.username };
48
+ }
49
+ async search(board, query, limit) {
50
+ const data = await this.board(board);
51
+ const lists = data.included?.lists ?? [];
52
+ const cards = new Map((data.included?.cards ?? []).map((c) => [c.id, c]));
53
+ let truncated = false;
54
+ // Endless lists have separate pages; cap work and disclose incomplete results.
55
+ for (const list of lists.filter((l) => l.type === 'archive' || l.type === 'trash')) {
56
+ let before;
57
+ for (let page = 0; page < 10; page++) {
58
+ const params = new URLSearchParams();
59
+ if (before) {
60
+ params.set('before', JSON.stringify(before));
61
+ }
62
+ const rows = (await this.transport.request(`lists/${list.id}/cards${params.size ? `?${params}` : ''}`))
63
+ .items ?? [];
64
+ if (!rows.length) {
65
+ break;
66
+ }
67
+ for (const row of rows) {
68
+ cards.set(row.id, row);
69
+ }
70
+ const last = rows.at(-1);
71
+ if (!last.listChangedAt || page === 9) {
72
+ truncated = true;
73
+ break;
74
+ }
75
+ const next = { id: last.id, listChangedAt: last.listChangedAt };
76
+ if (JSON.stringify(next) === JSON.stringify(before)) {
77
+ truncated = true;
78
+ break;
79
+ }
80
+ before = next;
81
+ }
82
+ }
83
+ const matches = [...cards.values()].filter((c) => c.name?.toLocaleLowerCase().includes(query.toLocaleLowerCase()));
84
+ return {
85
+ items: matches.slice(0, limit).map((c) => this.link(c)),
86
+ truncated: truncated || matches.length > limit,
87
+ };
88
+ }
89
+ async execute(operation, input) {
90
+ // The public method validates independently of MCP callers.
91
+ const args = { ...operationSchemas[operation].parse(input), operation };
92
+ switch (args.operation) {
93
+ case 'projects':
94
+ return {
95
+ items: ((await this.transport.request('projects')).items ?? []).map((i) => this.link(i, 'projects')),
96
+ };
97
+ case 'boards': {
98
+ const data = await this.transport.request('projects');
99
+ const projectId = args.project
100
+ ? resolveReference(this.url, args.project, data.items ?? [], 'projects').id
101
+ : undefined;
102
+ return {
103
+ items: (data.included?.boards ?? [])
104
+ .filter((b) => !projectId || b.projectId === projectId)
105
+ .map((b) => this.link(b, 'boards')),
106
+ };
107
+ }
108
+ case 'lists':
109
+ return { items: (await this.board(args.board)).included?.lists ?? [] };
110
+ case 'find_cards':
111
+ return this.search(args.board, args.query, args.limit);
112
+ case 'create_card': {
113
+ const board = await this.board(args.board);
114
+ const list = resolveReference(this.url, args.list, board.included?.lists ?? [], 'lists');
115
+ this.writableList(list);
116
+ const item = await this.writeItem(`lists/${list.id}/cards`, 'POST', {
117
+ name: args.name,
118
+ description: args.description,
119
+ type: args.type,
120
+ position: args.position,
121
+ });
122
+ return { item: this.link(item) };
123
+ }
124
+ default:
125
+ return this.executeCard(args);
126
+ }
127
+ }
128
+ async writeItem(path, method, body) {
129
+ return this.item(await this.transport.request(path, method, body));
130
+ }
131
+ async executeCard(args) {
132
+ const data = await this.card(args.card, args.board);
133
+ const card = this.item(data);
134
+ const url = this.link(card).url;
135
+ switch (args.operation) {
136
+ case 'read_card':
137
+ return {
138
+ item: this.link(card),
139
+ taskLists: data.included?.taskLists ?? [],
140
+ tasks: data.included?.tasks ?? [],
141
+ };
142
+ case 'edit_card': {
143
+ const item = await this.writeItem(`cards/${card.id}`, 'PATCH', {
144
+ name: args.name,
145
+ description: args.description,
146
+ });
147
+ return { item: this.link(item) };
148
+ }
149
+ case 'move_card': {
150
+ const board = await this.board(args.destinationBoard);
151
+ const list = resolveReference(this.url, args.list, board.included?.lists ?? [], 'lists');
152
+ this.writableList(list);
153
+ const item = await this.writeItem(`cards/${card.id}`, 'PATCH', {
154
+ boardId: this.item(board).id,
155
+ listId: list.id,
156
+ position: args.position,
157
+ });
158
+ return { item: this.link(item) };
159
+ }
160
+ case 'task_lists':
161
+ return { items: data.included?.taskLists ?? [], tasks: data.included?.tasks ?? [], url };
162
+ case 'create_task_list': {
163
+ const item = await this.writeItem(`cards/${card.id}/task-lists`, 'POST', {
164
+ name: args.name,
165
+ position: args.position,
166
+ });
167
+ return { item, url };
168
+ }
169
+ default:
170
+ return { item: await this.executeTask(args, data), url };
171
+ }
172
+ }
173
+ async executeTask(args, data) {
174
+ const taskList = resolveReference(this.url, args.taskList, data.included?.taskLists ?? [], 'task-lists');
175
+ switch (args.operation) {
176
+ case 'edit_task_list':
177
+ return this.writeItem(`task-lists/${taskList.id}`, 'PATCH', { name: args.name });
178
+ case 'add_task':
179
+ return this.writeItem(`task-lists/${taskList.id}/tasks`, 'POST', {
180
+ name: args.name,
181
+ position: args.position,
182
+ });
183
+ case 'complete_task': {
184
+ const task = resolveReference(this.url, args.task, (data.included?.tasks ?? []).filter((task) => task.taskListId === taskList.id), 'tasks');
185
+ return this.writeItem(`tasks/${task.id}`, 'PATCH', { isCompleted: args.isCompleted });
186
+ }
187
+ }
188
+ }
189
+ }
190
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EACL,gBAAgB,GAMjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAsB,MAAM,gBAAgB,CAAC;AAErE,MAAM,OAAO,YAAY;IACd,GAAG,CAAS;IACJ,SAAS,CAAkB;IAE5C,YAAY,OAAsB;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;IAChC,CAAC;IAEO,IAAI,CAAC,IAAc;QACzB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,IAAI,aAAa,CAAC,KAAK,EAAE,mDAAmD,CAAC,CAAC;QACtF,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,KAAa;QAC/B,MAAM,EAAE,GACN,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC;YACtC,gBAAgB,CACd,IAAI,CAAC,GAAG,EACR,KAAK,EACL,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EACjE,QAAQ,CACT,CAAC,EAAE,CAAC;QACP,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAChD,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,KAAa,EAAE,KAAc;QAC9C,IAAI,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,aAAa,CACrB,YAAY,EACZ,+DAA+D,CAChE,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;YACnD,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACpB,MAAM,IAAI,aAAa,CACrB,YAAY,EACZ,kFAAkF,CACnF,CAAC;YACJ,CAAC;YACD,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;QAClE,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IAEO,YAAY,CAAC,IAAY;QAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,aAAa,CACrB,YAAY,EACZ,iEAAiE,CAClE,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,IAAI,CAAC,IAAY,EAAE,IAAI,GAAG,OAAO;QACvC,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IACnE,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAAa,EAAE,KAAa;QAC9D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,+EAA+E;QAC/E,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,CAAC;YACnF,IAAI,MAAe,CAAC;YACpB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;gBACrC,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;gBACrC,IAAI,MAAM,EAAE,CAAC;oBACX,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC/C,CAAC;gBACD,MAAM,IAAI,GACR,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,EAAE,SAAS,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;qBACvF,KAAK,IAAI,EAAE,CAAC;gBACjB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACjB,MAAM;gBACR,CAAC;gBACD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACvB,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBACzB,CAAC;gBACD,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACtC,SAAS,GAAG,IAAI,CAAC;oBACjB,MAAM;gBACR,CAAC;gBACD,MAAM,IAAI,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;gBAChE,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;oBACpD,SAAS,GAAG,IAAI,CAAC;oBACjB,MAAM;gBACR,CAAC;gBACD,MAAM,GAAG,IAAI,CAAC;YAChB,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/C,CAAC,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,CAChE,CAAC;QACF,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACvD,SAAS,EAAE,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK;SAC/C,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO,CACX,SAAY,EACZ,KAAwB;QAExB,4DAA4D;QAC5D,MAAM,IAAI,GAAG,EAAE,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,SAAS,EAAqB,CAAC;QAC3F,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;YACvB,KAAK,UAAU;gBACb,OAAO;oBACL,KAAK,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACxE,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CACzB;iBACF,CAAC;YACJ,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBACtD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO;oBAC5B,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC,EAAE;oBAC3E,CAAC,CAAC,SAAS,CAAC;gBACd,OAAO;oBACL,KAAK,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC;yBACjC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC;yBACtD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;iBACtC,CAAC;YACJ,CAAC;YACD,KAAK,OAAO;gBACV,OAAO,EAAE,KAAK,EAAE,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;YACzE,KAAK,YAAY;gBACf,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YACzD,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC3C,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;gBACzF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBACxB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE;oBAClE,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB,CAAC,CAAC;gBACH,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,CAAC;YACD;gBACE,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,IAAY,EAAE,MAAwB,EAAE,IAAa;QAC3E,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IACrE,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,IAAgD;QACxE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;QAEhC,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;YACvB,KAAK,WAAW;gBACd,OAAO;oBACL,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;oBACrB,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE;oBACzC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;iBAClC,CAAC;YACJ,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE;oBAC7D,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,WAAW,EAAE,IAAI,CAAC,WAAW;iBAC9B,CAAC,CAAC;gBACH,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,CAAC;YACD,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;gBACtD,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;gBACzF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBACxB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE;oBAC7D,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE;oBAC5B,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB,CAAC,CAAC;gBACH,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,CAAC;YACD,KAAK,YAAY;gBACf,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC;YAC3F,KAAK,kBAAkB,CAAC,CAAC,CAAC;gBACxB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE;oBACvE,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB,CAAC,CAAC;gBACH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;YACvB,CAAC;YACD;gBACE,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QAC7D,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,IAAoD,EAAE,IAAc;QAC5F,MAAM,QAAQ,GAAG,gBAAgB,CAC/B,IAAI,CAAC,GAAG,EACR,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE,EAC9B,YAAY,CACb,CAAC;QAEF,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;YACvB,KAAK,gBAAgB;gBACnB,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACnF,KAAK,UAAU;gBACb,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,QAAQ,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE;oBAC/D,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB,CAAC,CAAC;YACL,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,MAAM,IAAI,GAAG,gBAAgB,CAC3B,IAAI,CAAC,GAAG,EACR,IAAI,CAAC,IAAI,EACT,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,EAAE,CAAC,EAC9E,OAAO,CACR,CAAC;gBACF,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACxF,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ export type ErrorCode = 'AUTHENTICATION' | 'PERMISSION' | 'VALIDATION' | 'NOT_FOUND' | 'AMBIGUOUS' | 'NETWORK' | 'UNCERTAIN_WRITE' | 'API' | 'STORAGE' | 'LOGIN';
2
+ export declare class PlanktonError extends Error {
3
+ code: ErrorCode;
4
+ details?: Record<string, unknown> | undefined;
5
+ constructor(code: ErrorCode, message: string, details?: Record<string, unknown> | undefined);
6
+ }
7
+ export declare function errorResult(error: unknown): {
8
+ ok: false;
9
+ error: {
10
+ details?: Record<string, unknown> | undefined;
11
+ code: ErrorCode;
12
+ message: string;
13
+ };
14
+ };
package/dist/errors.js ADDED
@@ -0,0 +1,23 @@
1
+ import { z } from 'zod';
2
+ export class PlanktonError extends Error {
3
+ code;
4
+ details;
5
+ constructor(code, message, details) {
6
+ super(message);
7
+ this.code = code;
8
+ this.details = details;
9
+ this.name = 'PlanktonError';
10
+ }
11
+ }
12
+ export function errorResult(error) {
13
+ const e = error instanceof PlanktonError
14
+ ? error
15
+ : error instanceof z.ZodError
16
+ ? new PlanktonError('VALIDATION', 'Invalid input. Check field types, lengths and required references.', { fields: error.issues.map((i) => i.path.join('.')) })
17
+ : new PlanktonError('API', 'Operation failed. Run plankton doctor for connection diagnostics.');
18
+ return {
19
+ ok: false,
20
+ error: { code: e.code, message: e.message, ...(e.details ? { details: e.details } : {}) },
21
+ };
22
+ }
23
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,MAAM,OAAO,aAAc,SAAQ,KAAK;IAE7B;IAEA;IAHT,YACS,IAAe,EACtB,OAAe,EACR,OAAiC;QAExC,KAAK,CAAC,OAAO,CAAC,CAAC;QAJR,SAAI,GAAJ,IAAI,CAAW;QAEf,YAAO,GAAP,OAAO,CAA0B;QAGxC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,CAAC,GACL,KAAK,YAAY,aAAa;QAC5B,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,QAAQ;YAC3B,CAAC,CAAC,IAAI,aAAa,CACf,YAAY,EACZ,oEAAoE,EACpE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CACtD;YACH,CAAC,CAAC,IAAI,aAAa,CACf,KAAK,EACL,mEAAmE,CACpE,CAAC;IACV,OAAO;QACL,EAAE,EAAE,KAAc;QAClB,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;KAC1F,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { PlankaClient } from './client.js';
2
+ export { PlanktonError, errorResult, type ErrorCode } from './errors.js';
3
+ export { normalizeUrl, sessionSchema, type Session } from './session.js';
4
+ export { operationSchemas, type Entity, type Operation, type OperationInput } from './schemas.js';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { PlankaClient } from './client.js';
2
+ export { PlanktonError, errorResult } from './errors.js';
3
+ export { normalizeUrl, sessionSchema } from './session.js';
4
+ export { operationSchemas } from './schemas.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAgB,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAoD,MAAM,cAAc,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Entity } from './schemas.js';
2
+ export declare function referenceId(baseUrl: string, value: string, kind: string): string | undefined;
3
+ export declare function resolveReference(baseUrl: string, value: string, items: Entity[], kind: string): Entity;
@@ -0,0 +1,32 @@
1
+ import { PlanktonError } from './errors.js';
2
+ export function referenceId(baseUrl, value, kind) {
3
+ if (/^\d+$/.test(value)) {
4
+ return value;
5
+ }
6
+ if (/^https?:\/\//i.test(value)) {
7
+ const url = new URL(value);
8
+ const base = new URL(baseUrl);
9
+ const prefix = `${base.pathname.replace(/\/$/, '')}/${kind}/`;
10
+ if (url.origin !== base.origin ||
11
+ !url.pathname.startsWith(prefix) ||
12
+ !/^\d+$/.test(url.pathname.slice(prefix.length))) {
13
+ throw new PlanktonError('VALIDATION', `Use a ${kind} link from the active Planka instance.`);
14
+ }
15
+ return url.pathname.slice(prefix.length);
16
+ }
17
+ return undefined;
18
+ }
19
+ export function resolveReference(baseUrl, value, items, kind) {
20
+ const id = referenceId(baseUrl, value, kind);
21
+ const matches = items.filter((i) => id ? i.id === id : i.name?.toLocaleLowerCase() === value.toLocaleLowerCase());
22
+ if (!matches.length) {
23
+ throw new PlanktonError('NOT_FOUND', `No matching ${kind} in this scope.`);
24
+ }
25
+ if (matches.length > 1) {
26
+ throw new PlanktonError('AMBIGUOUS', `Multiple ${kind} match. Choose an ID.`, {
27
+ choices: matches.map((i) => ({ id: i.id, name: i.name })),
28
+ });
29
+ }
30
+ return matches[0];
31
+ }
32
+ //# sourceMappingURL=references.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.js","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,KAAa,EAAE,IAAY;IACtE,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC;QAC9D,IACE,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAC1B,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;YAChC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAChD,CAAC;YACD,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,SAAS,IAAI,wCAAwC,CAAC,CAAC;QAC/F,CAAC;QACD,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,KAAa,EACb,KAAe,EACf,IAAY;IAEZ,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACjC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,KAAK,CAAC,iBAAiB,EAAE,CAC7E,CAAC;IACF,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,IAAI,aAAa,CAAC,WAAW,EAAE,eAAe,IAAI,iBAAiB,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,aAAa,CAAC,WAAW,EAAE,YAAY,IAAI,uBAAuB,EAAE;YAC5E,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;SAC1D,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAE,CAAC;AACrB,CAAC"}
@@ -0,0 +1,101 @@
1
+ import { z } from 'zod';
2
+ declare const entitySchema: z.ZodObject<{
3
+ id: z.ZodString;
4
+ name: z.ZodOptional<z.ZodString>;
5
+ }, z.core.$catchall<z.ZodUnknown>>;
6
+ export type Entity = z.infer<typeof entitySchema>;
7
+ export declare const envelopeSchema: z.ZodObject<{
8
+ item: z.ZodOptional<z.ZodObject<{
9
+ id: z.ZodString;
10
+ name: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$catchall<z.ZodUnknown>>>;
12
+ items: z.ZodOptional<z.ZodArray<z.ZodObject<{
13
+ id: z.ZodString;
14
+ name: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$catchall<z.ZodUnknown>>>>;
16
+ included: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodObject<{
17
+ id: z.ZodString;
18
+ name: z.ZodOptional<z.ZodString>;
19
+ }, z.core.$catchall<z.ZodUnknown>>>>>;
20
+ }, z.core.$strip>;
21
+ export type Envelope = z.infer<typeof envelopeSchema>;
22
+ export declare const operationSchemas: {
23
+ projects: z.ZodObject<{}, z.core.$strict>;
24
+ boards: z.ZodObject<{
25
+ project: z.ZodOptional<z.ZodString>;
26
+ }, z.core.$strict>;
27
+ lists: z.ZodObject<{
28
+ board: z.ZodString;
29
+ }, z.core.$strict>;
30
+ find_cards: z.ZodObject<{
31
+ board: z.ZodString;
32
+ query: z.ZodString;
33
+ limit: z.ZodDefault<z.ZodNumber>;
34
+ }, z.core.$strict>;
35
+ read_card: z.ZodObject<{
36
+ card: z.ZodString;
37
+ board: z.ZodOptional<z.ZodString>;
38
+ }, z.core.$strict>;
39
+ create_card: z.ZodObject<{
40
+ board: z.ZodString;
41
+ list: z.ZodString;
42
+ name: z.ZodString;
43
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
44
+ type: z.ZodDefault<z.ZodEnum<{
45
+ project: "project";
46
+ story: "story";
47
+ }>>;
48
+ position: z.ZodDefault<z.ZodNumber>;
49
+ }, z.core.$strict>;
50
+ edit_card: z.ZodObject<{
51
+ card: z.ZodString;
52
+ board: z.ZodOptional<z.ZodString>;
53
+ name: z.ZodOptional<z.ZodString>;
54
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
55
+ }, z.core.$strict>;
56
+ move_card: z.ZodObject<{
57
+ card: z.ZodString;
58
+ board: z.ZodOptional<z.ZodString>;
59
+ destinationBoard: z.ZodString;
60
+ list: z.ZodString;
61
+ position: z.ZodDefault<z.ZodNumber>;
62
+ }, z.core.$strict>;
63
+ task_lists: z.ZodObject<{
64
+ card: z.ZodString;
65
+ board: z.ZodOptional<z.ZodString>;
66
+ }, z.core.$strict>;
67
+ create_task_list: z.ZodObject<{
68
+ card: z.ZodString;
69
+ board: z.ZodOptional<z.ZodString>;
70
+ name: z.ZodString;
71
+ position: z.ZodDefault<z.ZodNumber>;
72
+ }, z.core.$strict>;
73
+ edit_task_list: z.ZodObject<{
74
+ card: z.ZodString;
75
+ board: z.ZodOptional<z.ZodString>;
76
+ taskList: z.ZodString;
77
+ name: z.ZodString;
78
+ }, z.core.$strict>;
79
+ add_task: z.ZodObject<{
80
+ card: z.ZodString;
81
+ board: z.ZodOptional<z.ZodString>;
82
+ taskList: z.ZodString;
83
+ name: z.ZodString;
84
+ position: z.ZodDefault<z.ZodNumber>;
85
+ }, z.core.$strict>;
86
+ complete_task: z.ZodObject<{
87
+ card: z.ZodString;
88
+ board: z.ZodOptional<z.ZodString>;
89
+ taskList: z.ZodString;
90
+ task: z.ZodString;
91
+ isCompleted: z.ZodDefault<z.ZodBoolean>;
92
+ }, z.core.$strict>;
93
+ };
94
+ export type Operation = keyof typeof operationSchemas;
95
+ export type OperationInput<K extends Operation> = z.input<(typeof operationSchemas)[K]>;
96
+ export type ParsedOperation = {
97
+ [K in Operation]: z.output<(typeof operationSchemas)[K]> & {
98
+ operation: K;
99
+ };
100
+ }[Operation];
101
+ export {};
@@ -0,0 +1,66 @@
1
+ import { z } from 'zod';
2
+ const entitySchema = z
3
+ .object({ id: z.string().regex(/^\d+$/), name: z.string().optional() })
4
+ .catchall(z.unknown());
5
+ export const envelopeSchema = z.object({
6
+ item: entitySchema.optional(),
7
+ items: z.array(entitySchema).optional(),
8
+ included: z.record(z.string(), z.array(entitySchema)).optional(),
9
+ });
10
+ const ref = z.string().trim().min(1).max(2048);
11
+ const name = z.string().trim().min(1).max(1024);
12
+ const position = z.number().finite().nonnegative().default(65535);
13
+ const description = z.string().min(1).max(1048576).nullable();
14
+ export const operationSchemas = {
15
+ projects: z.object({}).strict(),
16
+ boards: z.object({ project: ref.optional() }).strict(),
17
+ lists: z.object({ board: ref }).strict(),
18
+ find_cards: z
19
+ .object({
20
+ board: ref,
21
+ query: z.string().trim().min(1).max(128),
22
+ limit: z.number().int().min(1).max(100).default(25),
23
+ })
24
+ .strict(),
25
+ read_card: z.object({ card: ref, board: ref.optional() }).strict(),
26
+ create_card: z
27
+ .object({
28
+ board: ref,
29
+ list: ref,
30
+ name,
31
+ description: description.optional(),
32
+ type: z.enum(['project', 'story']).default('project'),
33
+ position,
34
+ })
35
+ .strict(),
36
+ edit_card: z
37
+ .object({
38
+ card: ref,
39
+ board: ref.optional(),
40
+ name: name.optional(),
41
+ description: description.optional(),
42
+ })
43
+ .strict()
44
+ .refine((v) => v.name !== undefined || v.description !== undefined),
45
+ move_card: z
46
+ .object({ card: ref, board: ref.optional(), destinationBoard: ref, list: ref, position })
47
+ .strict(),
48
+ task_lists: z.object({ card: ref, board: ref.optional() }).strict(),
49
+ create_task_list: z
50
+ .object({ card: ref, board: ref.optional(), name: name.max(128), position })
51
+ .strict(),
52
+ edit_task_list: z
53
+ .object({ card: ref, board: ref.optional(), taskList: ref, name: name.max(128) })
54
+ .strict(),
55
+ add_task: z.object({ card: ref, board: ref.optional(), taskList: ref, name, position }).strict(),
56
+ complete_task: z
57
+ .object({
58
+ card: ref,
59
+ board: ref.optional(),
60
+ taskList: ref,
61
+ task: ref,
62
+ isCompleted: z.boolean().default(true),
63
+ })
64
+ .strict(),
65
+ };
66
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+ export declare const sessionSchema: z.ZodObject<{
3
+ accessToken: z.ZodString;
4
+ httpOnlyToken: z.ZodOptional<z.ZodString>;
5
+ }, z.core.$strict>;
6
+ export type Session = z.infer<typeof sessionSchema>;
7
+ export declare function normalizeUrl(value: string): string;
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ import { PlanktonError } from './errors.js';
3
+ const token = z
4
+ .string()
5
+ .min(1)
6
+ .max(16384)
7
+ .regex(/^[A-Za-z0-9._~+%/=-]+$/);
8
+ export const sessionSchema = z
9
+ .object({ accessToken: token, httpOnlyToken: token.optional() })
10
+ .strict();
11
+ export function normalizeUrl(value) {
12
+ let url;
13
+ try {
14
+ url = new URL(value);
15
+ }
16
+ catch {
17
+ throw new PlanktonError('VALIDATION', 'Enter an absolute Planka URL.');
18
+ }
19
+ if (!['http:', 'https:'].includes(url.protocol) ||
20
+ url.username ||
21
+ url.password ||
22
+ url.search ||
23
+ url.hash) {
24
+ throw new PlanktonError('VALIDATION', 'Use an HTTP(S) Planka URL without credentials, query or fragment.');
25
+ }
26
+ if (url.protocol !== 'https:' && !['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) {
27
+ throw new PlanktonError('VALIDATION', 'Use HTTPS for remote Planka instances.');
28
+ }
29
+ return url.href.replace(/\/+$/, '');
30
+ }
31
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,MAAM,KAAK,GAAG,CAAC;KACZ,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,CAAC;KACN,GAAG,CAAC,KAAK,CAAC;KACV,KAAK,CAAC,wBAAwB,CAAC,CAAC;AACnC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC;KAC3B,MAAM,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC;KAC/D,MAAM,EAAE,CAAC;AAGZ,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,+BAA+B,CAAC,CAAC;IACzE,CAAC;IACD,IACE,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC3C,GAAG,CAAC,QAAQ;QACZ,GAAG,CAAC,QAAQ;QACZ,GAAG,CAAC,MAAM;QACV,GAAG,CAAC,IAAI,EACR,CAAC;QACD,MAAM,IAAI,aAAa,CACrB,YAAY,EACZ,mEAAmE,CACpE,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7F,MAAM,IAAI,aAAa,CAAC,YAAY,EAAE,wCAAwC,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type Session } from './session.js';
2
+ import { type Envelope } from './schemas.js';
3
+ export interface ClientOptions {
4
+ url: string;
5
+ session: Session;
6
+ fetch?: typeof fetch;
7
+ timeoutMs?: number;
8
+ }
9
+ export declare class PlankaTransport {
10
+ readonly url: string;
11
+ private readonly session;
12
+ private readonly fetcher;
13
+ private readonly timeoutMs;
14
+ constructor(options: ClientOptions);
15
+ request(path: string, method?: string, body?: unknown): Promise<Envelope>;
16
+ }
@@ -0,0 +1,67 @@
1
+ import { PlanktonError } from './errors.js';
2
+ import { normalizeUrl, sessionSchema } from './session.js';
3
+ import { envelopeSchema } from './schemas.js';
4
+ export class PlankaTransport {
5
+ url;
6
+ session;
7
+ fetcher;
8
+ timeoutMs;
9
+ constructor(options) {
10
+ this.url = normalizeUrl(options.url);
11
+ this.session = sessionSchema.parse(options.session);
12
+ this.fetcher = options.fetch ?? fetch;
13
+ this.timeoutMs = options.timeoutMs ?? 15000;
14
+ }
15
+ async request(path, method = 'GET', body) {
16
+ const write = method !== 'GET';
17
+ try {
18
+ const response = await this.fetcher(`${this.url}/api/${path}`, {
19
+ method,
20
+ redirect: 'error',
21
+ signal: AbortSignal.timeout(this.timeoutMs),
22
+ headers: {
23
+ Accept: 'application/json',
24
+ Authorization: `Bearer ${this.session.accessToken}`,
25
+ ...(this.session.httpOnlyToken
26
+ ? { Cookie: `httpOnlyToken=${this.session.httpOnlyToken}` }
27
+ : {}),
28
+ ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
29
+ },
30
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
31
+ });
32
+ if (response.status === 401) {
33
+ throw new PlanktonError('AUTHENTICATION', 'Planka session expired or invalid. Run plankton login.');
34
+ }
35
+ if (response.status === 403) {
36
+ throw new PlanktonError('PERMISSION', 'Planka denied this operation. Check the account’s board permissions.');
37
+ }
38
+ if (response.status === 404) {
39
+ throw new PlanktonError('NOT_FOUND', 'Resource not found or not accessible to this account.');
40
+ }
41
+ if ([400, 422].includes(response.status)) {
42
+ throw new PlanktonError('VALIDATION', 'Planka rejected the fields. Check the resource type and values.');
43
+ }
44
+ if (!response.ok) {
45
+ throw new PlanktonError(write && response.status >= 500 ? 'UNCERTAIN_WRITE' : 'API', write && response.status >= 500
46
+ ? 'Planka failed during a write. Read the resource before trying again; the change may have completed.'
47
+ : `Planka returned HTTP ${response.status}.`, { status: response.status });
48
+ }
49
+ const parsed = envelopeSchema.safeParse(await response.json());
50
+ if (!parsed.success ||
51
+ (write && !parsed.data.item) ||
52
+ (path === 'projects' && !parsed.data.items)) {
53
+ throw new Error('Invalid response');
54
+ }
55
+ return parsed.data;
56
+ }
57
+ catch (error) {
58
+ if (error instanceof PlanktonError) {
59
+ throw error;
60
+ }
61
+ throw new PlanktonError(write ? 'UNCERTAIN_WRITE' : 'NETWORK', write
62
+ ? 'The write outcome is unknown. Read the resource before retrying; do not repeat the write blindly.'
63
+ : 'Could not read Planka. Check the URL, network and server response.');
64
+ }
65
+ }
66
+ }
67
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "author": "Evan Kim",
3
+ "name": "@evanston/plankton-core",
4
+ "version": "0.1.0",
5
+ "description": "Local Planka integration core",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "engines": {
9
+ "node": ">=22"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "zod": "^4.0.0"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.json",
27
+ "typecheck": "tsc -p tsconfig.json --noEmit"
28
+ }
29
+ }