@plandesk/api 0.7.0 → 0.8.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/events.d.ts +15 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/routes/documents.js +2 -0
- package/dist/routes/folders.d.ts +4 -0
- package/dist/routes/folders.js +69 -0
- package/dist/routes/projects.js +12 -3
- package/dist/routes/tags.d.ts +4 -0
- package/dist/routes/tags.js +62 -0
- package/dist/routes/tasks.d.ts +1 -0
- package/dist/routes/tasks.js +9 -1
- package/dist/serialize.d.ts +30 -2
- package/dist/serialize.js +66 -1
- package/dist/server.js +5 -1
- package/dist/services/canvas.d.ts +2 -0
- package/dist/services/canvas.js +1 -1
- package/dist/services/documents.d.ts +5 -1
- package/dist/services/documents.js +31 -2
- package/dist/services/folders.d.ts +27 -0
- package/dist/services/folders.js +107 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.js +6 -0
- package/dist/services/projects.js +5 -2
- package/dist/services/tags.d.ts +27 -0
- package/dist/services/tags.js +79 -0
- package/dist/services/tasks.d.ts +10 -1
- package/dist/services/tasks.js +70 -26
- package/package.json +3 -3
- package/web/assets/{index-cyyf9-Uw.js → index-DSmNB4RA.js} +61 -60
- package/web/index.html +1 -1
package/dist/events.d.ts
CHANGED
|
@@ -24,6 +24,20 @@ export type CommentUpdatedEvent = {
|
|
|
24
24
|
documentId: string;
|
|
25
25
|
projectId: string;
|
|
26
26
|
};
|
|
27
|
+
export type FolderCreatedEvent = {
|
|
28
|
+
type: 'folder_created';
|
|
29
|
+
folderId: string;
|
|
30
|
+
projectId: string;
|
|
31
|
+
};
|
|
32
|
+
export type FolderUpdatedEvent = {
|
|
33
|
+
type: 'folder_updated';
|
|
34
|
+
folderId: string;
|
|
35
|
+
projectId: string;
|
|
36
|
+
};
|
|
37
|
+
export type TagUpdatedEvent = {
|
|
38
|
+
type: 'tag_updated';
|
|
39
|
+
projectId: string;
|
|
40
|
+
};
|
|
27
41
|
export type NoteCreatedEvent = {
|
|
28
42
|
type: 'note_created';
|
|
29
43
|
noteId: string;
|
|
@@ -53,7 +67,7 @@ export type SubmissionsPulledEvent = {
|
|
|
53
67
|
type: 'submissions_pulled';
|
|
54
68
|
projectId: string;
|
|
55
69
|
};
|
|
56
|
-
export type PlankDeskEvent = TaskUpdatedEvent | CanvasUpdatedEvent | DocumentCreatedEvent | CommentCreatedEvent | CommentUpdatedEvent | NoteCreatedEvent | NoteUpdatedEvent | AgentRunStartedEvent | AgentRunProgressEvent | AgentRunCompletedEvent | SubmissionsPulledEvent;
|
|
70
|
+
export type PlankDeskEvent = TaskUpdatedEvent | TagUpdatedEvent | CanvasUpdatedEvent | DocumentCreatedEvent | CommentCreatedEvent | CommentUpdatedEvent | FolderCreatedEvent | FolderUpdatedEvent | NoteCreatedEvent | NoteUpdatedEvent | AgentRunStartedEvent | AgentRunProgressEvent | AgentRunCompletedEvent | SubmissionsPulledEvent;
|
|
57
71
|
export type EventListener = (event: PlankDeskEvent) => void;
|
|
58
72
|
export type EventBus = {
|
|
59
73
|
subscribe(listener: EventListener): () => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -6,13 +6,17 @@ export { createServices, type Services, type ServicesDeps } from './services/ind
|
|
|
6
6
|
export { createEventBus, type EventBus, type PlankDeskEvent } from './events.js';
|
|
7
7
|
export type { ProjectService } from './services/projects.js';
|
|
8
8
|
export type { TaskService } from './services/tasks.js';
|
|
9
|
+
export type { TagService } from './services/tags.js';
|
|
9
10
|
export type { CanvasService } from './services/canvas.js';
|
|
10
11
|
export type { DocumentService } from './services/documents.js';
|
|
12
|
+
export type { FolderService } from './services/folders.js';
|
|
11
13
|
export type { NoteService } from './services/notes.js';
|
|
12
14
|
export type { CommentService } from './services/comments.js';
|
|
13
15
|
export type { AgentRunService } from './services/agent-runs.js';
|
|
14
16
|
export { InvalidDocumentError } from './services/documents.js';
|
|
17
|
+
export { InvalidFolderError } from './services/folders.js';
|
|
15
18
|
export { InvalidNoteError } from './services/notes.js';
|
|
19
|
+
export { InvalidTagError } from './services/tags.js';
|
|
16
20
|
export { InvalidCommentError } from './services/comments.js';
|
|
17
21
|
export { InvalidCanvasError } from './services/canvas.js';
|
|
18
22
|
export { InvalidAgentRunError } from './services/agent-runs.js';
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,9 @@ export { mountStatic } from './static.js';
|
|
|
5
5
|
export { createServices } from './services/index.js';
|
|
6
6
|
export { createEventBus } from './events.js';
|
|
7
7
|
export { InvalidDocumentError } from './services/documents.js';
|
|
8
|
+
export { InvalidFolderError } from './services/folders.js';
|
|
8
9
|
export { InvalidNoteError } from './services/notes.js';
|
|
10
|
+
export { InvalidTagError } from './services/tags.js';
|
|
9
11
|
export { InvalidCommentError } from './services/comments.js';
|
|
10
12
|
export { InvalidCanvasError } from './services/canvas.js';
|
|
11
13
|
export { InvalidAgentRunError } from './services/agent-runs.js';
|
package/dist/routes/documents.js
CHANGED
|
@@ -37,6 +37,7 @@ export function createDocumentsRouter(documentService) {
|
|
|
37
37
|
body: body.body,
|
|
38
38
|
statusLine: body.status_line,
|
|
39
39
|
parentId: body.parent_id,
|
|
40
|
+
folderId: body.folder_id,
|
|
40
41
|
linkedTaskId: resolveLinkedTaskId(body),
|
|
41
42
|
});
|
|
42
43
|
if (!document) {
|
|
@@ -67,6 +68,7 @@ export function createDocumentsRouter(documentService) {
|
|
|
67
68
|
...(body.body !== undefined ? { body: body.body } : {}),
|
|
68
69
|
...(body.status_line !== undefined ? { statusLine: body.status_line } : {}),
|
|
69
70
|
...(body.parent_id !== undefined ? { parentId: body.parent_id } : {}),
|
|
71
|
+
...(body.folder_id !== undefined ? { folderId: body.folder_id } : {}),
|
|
70
72
|
...(linkedTaskId !== undefined ? { linkedTaskId } : {}),
|
|
71
73
|
});
|
|
72
74
|
if (!document) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { InvalidFolderError } from '../services/folders.js';
|
|
3
|
+
export function createFoldersRouter(folderService) {
|
|
4
|
+
const router = new Hono();
|
|
5
|
+
router.get('/projects/:id/folders', (c) => {
|
|
6
|
+
const folders = folderService.list(c.req.param('id'));
|
|
7
|
+
if (!folders) {
|
|
8
|
+
return c.json({ error: 'not_found' }, 404);
|
|
9
|
+
}
|
|
10
|
+
return c.json(folders);
|
|
11
|
+
});
|
|
12
|
+
router.post('/projects/:id/folders', async (c) => {
|
|
13
|
+
const body = await c.req.json();
|
|
14
|
+
if (typeof body.name !== 'string' || body.name.trim() === '') {
|
|
15
|
+
return c.json({ error: 'invalid_argument' }, 400);
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const folder = folderService.create(c.req.param('id'), {
|
|
19
|
+
name: body.name,
|
|
20
|
+
parentFolderId: body.parent_folder_id,
|
|
21
|
+
});
|
|
22
|
+
if (!folder) {
|
|
23
|
+
return c.json({ error: 'not_found' }, 404);
|
|
24
|
+
}
|
|
25
|
+
return c.json(folder, 201);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error instanceof InvalidFolderError) {
|
|
29
|
+
return c.json({ error: 'invalid_argument' }, 400);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
router.get('/folders/:id', (c) => {
|
|
35
|
+
const folder = folderService.get(c.req.param('id'));
|
|
36
|
+
if (!folder) {
|
|
37
|
+
return c.json({ error: 'not_found' }, 404);
|
|
38
|
+
}
|
|
39
|
+
return c.json(folder);
|
|
40
|
+
});
|
|
41
|
+
router.patch('/folders/:id', async (c) => {
|
|
42
|
+
const body = await c.req.json();
|
|
43
|
+
try {
|
|
44
|
+
const folder = folderService.update(c.req.param('id'), {
|
|
45
|
+
...(body.name !== undefined ? { name: body.name } : {}),
|
|
46
|
+
...(body.parent_folder_id !== undefined ? { parentFolderId: body.parent_folder_id } : {}),
|
|
47
|
+
});
|
|
48
|
+
if (!folder) {
|
|
49
|
+
return c.json({ error: 'not_found' }, 404);
|
|
50
|
+
}
|
|
51
|
+
return c.json(folder);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error instanceof InvalidFolderError) {
|
|
55
|
+
return c.json({ error: 'invalid_argument' }, 400);
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
router.delete('/folders/:id', (c) => {
|
|
61
|
+
const deleted = folderService.delete(c.req.param('id'));
|
|
62
|
+
if (!deleted) {
|
|
63
|
+
return c.json({ error: 'not_found' }, 404);
|
|
64
|
+
}
|
|
65
|
+
return c.body(null, 204);
|
|
66
|
+
});
|
|
67
|
+
return router;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=folders.js.map
|
package/dist/routes/projects.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { InvalidTaskStatusError, isTaskStatus } from '@plandesk/db';
|
|
3
|
+
import { InvalidTagError } from '../services/tags.js';
|
|
4
|
+
import { isStringArray } from './tasks.js';
|
|
3
5
|
import { parsePaginationParams } from '../serialize.js';
|
|
4
6
|
export function createProjectsRouter(projectService, taskService) {
|
|
5
7
|
const router = new Hono();
|
|
@@ -57,6 +59,9 @@ export function createProjectsRouter(projectService, taskService) {
|
|
|
57
59
|
if (body.status !== undefined && !isTaskStatus(body.status)) {
|
|
58
60
|
return c.json({ error: 'invalid_argument' }, 400);
|
|
59
61
|
}
|
|
62
|
+
if (body.tags !== undefined && !isStringArray(body.tags)) {
|
|
63
|
+
return c.json({ error: 'invalid_argument' }, 400);
|
|
64
|
+
}
|
|
60
65
|
let dueDate;
|
|
61
66
|
if (body.due_date !== undefined && body.due_date !== null) {
|
|
62
67
|
dueDate = new Date(body.due_date);
|
|
@@ -76,6 +81,7 @@ export function createProjectsRouter(projectService, taskService) {
|
|
|
76
81
|
...(body.y !== undefined ? { y: body.y } : {}),
|
|
77
82
|
...(body.assignee !== undefined ? { assignee: body.assignee } : {}),
|
|
78
83
|
...(dueDate !== undefined ? { dueDate } : {}),
|
|
84
|
+
...(body.tags !== undefined ? { tags: body.tags } : {}),
|
|
79
85
|
});
|
|
80
86
|
if (!task) {
|
|
81
87
|
return c.json({ error: 'not_found' }, 404);
|
|
@@ -83,7 +89,7 @@ export function createProjectsRouter(projectService, taskService) {
|
|
|
83
89
|
return c.json(task, 201);
|
|
84
90
|
}
|
|
85
91
|
catch (error) {
|
|
86
|
-
if (error instanceof InvalidTaskStatusError) {
|
|
92
|
+
if (error instanceof InvalidTaskStatusError || error instanceof InvalidTagError) {
|
|
87
93
|
return c.json({ error: 'invalid_argument' }, 400);
|
|
88
94
|
}
|
|
89
95
|
throw error;
|
|
@@ -96,14 +102,17 @@ export function createProjectsRouter(projectService, taskService) {
|
|
|
96
102
|
return c.json({ error: 'invalid_argument' }, 400);
|
|
97
103
|
}
|
|
98
104
|
const status = c.req.query('status');
|
|
99
|
-
|
|
105
|
+
// Repeated ?tag= params filter with OR semantics (task matches if it has
|
|
106
|
+
// ANY of the given tags).
|
|
107
|
+
const tags = c.req.queries('tag');
|
|
108
|
+
const tasks = taskService.listByProject(c.req.param('id'), { status, ...(tags !== undefined && tags.length > 0 ? { tags } : {}) }, pagination);
|
|
100
109
|
if (!tasks) {
|
|
101
110
|
return c.json({ error: 'not_found' }, 404);
|
|
102
111
|
}
|
|
103
112
|
return c.json(tasks);
|
|
104
113
|
}
|
|
105
114
|
catch (error) {
|
|
106
|
-
if (error instanceof InvalidTaskStatusError) {
|
|
115
|
+
if (error instanceof InvalidTaskStatusError || error instanceof InvalidTagError) {
|
|
107
116
|
return c.json({ error: 'invalid_argument' }, 400);
|
|
108
117
|
}
|
|
109
118
|
throw error;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { InvalidTagError } from '../services/tags.js';
|
|
3
|
+
export function createTagsRouter(tagService) {
|
|
4
|
+
const router = new Hono();
|
|
5
|
+
router.get('/projects/:id/tags', (c) => {
|
|
6
|
+
const tags = tagService.list(c.req.param('id'));
|
|
7
|
+
if (!tags) {
|
|
8
|
+
return c.json({ error: 'not_found' }, 404);
|
|
9
|
+
}
|
|
10
|
+
return c.json(tags);
|
|
11
|
+
});
|
|
12
|
+
router.post('/projects/:id/tags', async (c) => {
|
|
13
|
+
const body = await c.req.json();
|
|
14
|
+
if (typeof body.name !== 'string' || body.name.trim() === '') {
|
|
15
|
+
return c.json({ error: 'invalid_argument' }, 400);
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const tag = tagService.create(c.req.param('id'), {
|
|
19
|
+
name: body.name,
|
|
20
|
+
color: body.color,
|
|
21
|
+
});
|
|
22
|
+
if (!tag) {
|
|
23
|
+
return c.json({ error: 'not_found' }, 404);
|
|
24
|
+
}
|
|
25
|
+
return c.json(tag, 201);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error instanceof InvalidTagError) {
|
|
29
|
+
return c.json({ error: 'invalid_argument' }, 400);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
router.patch('/tags/:id', async (c) => {
|
|
35
|
+
const body = await c.req.json();
|
|
36
|
+
try {
|
|
37
|
+
const tag = tagService.update(c.req.param('id'), {
|
|
38
|
+
...(body.name !== undefined ? { name: body.name } : {}),
|
|
39
|
+
...(body.color !== undefined ? { color: body.color } : {}),
|
|
40
|
+
});
|
|
41
|
+
if (!tag) {
|
|
42
|
+
return c.json({ error: 'not_found' }, 404);
|
|
43
|
+
}
|
|
44
|
+
return c.json(tag);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof InvalidTagError) {
|
|
48
|
+
return c.json({ error: 'invalid_argument' }, 400);
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
router.delete('/tags/:id', (c) => {
|
|
54
|
+
const deleted = tagService.delete(c.req.param('id'));
|
|
55
|
+
if (!deleted) {
|
|
56
|
+
return c.json({ error: 'not_found' }, 404);
|
|
57
|
+
}
|
|
58
|
+
return c.body(null, 204);
|
|
59
|
+
});
|
|
60
|
+
return router;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=tags.js.map
|
package/dist/routes/tasks.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import type { TaskService } from '../services/tasks.js';
|
|
3
|
+
export declare function isStringArray(value: unknown): value is string[];
|
|
3
4
|
export declare function createTasksRouter(taskService: TaskService): Hono;
|
|
4
5
|
//# sourceMappingURL=tasks.d.ts.map
|
package/dist/routes/tasks.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { InvalidTaskStatusError, isTaskStatus } from '@plandesk/db';
|
|
3
|
+
import { InvalidTagError } from '../services/tags.js';
|
|
4
|
+
export function isStringArray(value) {
|
|
5
|
+
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
6
|
+
}
|
|
3
7
|
export function createTasksRouter(taskService) {
|
|
4
8
|
const router = new Hono();
|
|
5
9
|
router.patch('/tasks/:id', async (c) => {
|
|
@@ -7,6 +11,9 @@ export function createTasksRouter(taskService) {
|
|
|
7
11
|
if (body.status !== undefined && !isTaskStatus(body.status)) {
|
|
8
12
|
return c.json({ error: 'invalid_argument' }, 400);
|
|
9
13
|
}
|
|
14
|
+
if (body.tags !== undefined && !isStringArray(body.tags)) {
|
|
15
|
+
return c.json({ error: 'invalid_argument' }, 400);
|
|
16
|
+
}
|
|
10
17
|
try {
|
|
11
18
|
const task = taskService.update(c.req.param('id'), {
|
|
12
19
|
...(body.label !== undefined ? { label: body.label } : {}),
|
|
@@ -14,6 +21,7 @@ export function createTasksRouter(taskService) {
|
|
|
14
21
|
...(body.description !== undefined ? { description: body.description } : {}),
|
|
15
22
|
...(body.x !== undefined ? { x: body.x } : {}),
|
|
16
23
|
...(body.y !== undefined ? { y: body.y } : {}),
|
|
24
|
+
...(body.tags !== undefined ? { tags: body.tags } : {}),
|
|
17
25
|
});
|
|
18
26
|
if (!task) {
|
|
19
27
|
return c.json({ error: 'not_found' }, 404);
|
|
@@ -21,7 +29,7 @@ export function createTasksRouter(taskService) {
|
|
|
21
29
|
return c.json(task);
|
|
22
30
|
}
|
|
23
31
|
catch (error) {
|
|
24
|
-
if (error instanceof InvalidTaskStatusError) {
|
|
32
|
+
if (error instanceof InvalidTaskStatusError || error instanceof InvalidTagError) {
|
|
25
33
|
return c.json({ error: 'invalid_argument' }, 400);
|
|
26
34
|
}
|
|
27
35
|
throw error;
|
package/dist/serialize.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentRun, AgentRunEvent, Document, DocumentComment, Edge, Note, Project, Task, TaskStatus } from '@plandesk/db';
|
|
1
|
+
import type { AgentRun, AgentRunEvent, Document, DocumentComment, Edge, Folder, Note, Project, Tag, Task, TaskStatus } from '@plandesk/db';
|
|
2
2
|
export type PaginationParams = {
|
|
3
3
|
limit?: number;
|
|
4
4
|
offset?: number;
|
|
@@ -21,7 +21,16 @@ export declare function serializeProjectDetail(project: Project, summary: TaskSt
|
|
|
21
21
|
created_at: string;
|
|
22
22
|
updated_at: string;
|
|
23
23
|
};
|
|
24
|
-
export
|
|
24
|
+
export type SerializedTag = {
|
|
25
|
+
id: string;
|
|
26
|
+
project_id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
color: string | null;
|
|
29
|
+
created_at: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function serializeTag(tag: Tag): SerializedTag;
|
|
32
|
+
export declare function serializeTask(task: Task, tags?: Tag[]): {
|
|
33
|
+
tags?: SerializedTag[] | undefined;
|
|
25
34
|
id: string;
|
|
26
35
|
project_id: string;
|
|
27
36
|
label: string;
|
|
@@ -41,6 +50,7 @@ export type SerializedDocument = {
|
|
|
41
50
|
body: string | null;
|
|
42
51
|
status_line: string | null;
|
|
43
52
|
parent_id: string | null;
|
|
53
|
+
folder_id: string | null;
|
|
44
54
|
linked_task_id: string | null;
|
|
45
55
|
created_at: string;
|
|
46
56
|
updated_at: string;
|
|
@@ -68,6 +78,24 @@ export type SerializedComment = {
|
|
|
68
78
|
};
|
|
69
79
|
export declare function serializeComment(comment: DocumentComment): SerializedComment;
|
|
70
80
|
export declare function buildDocumentTree(documents: Document[]): SerializedDocumentTree[];
|
|
81
|
+
export type SerializedFolder = {
|
|
82
|
+
id: string;
|
|
83
|
+
project_id: string;
|
|
84
|
+
name: string;
|
|
85
|
+
parent_folder_id: string | null;
|
|
86
|
+
created_at: string;
|
|
87
|
+
updated_at: string;
|
|
88
|
+
};
|
|
89
|
+
export declare function serializeFolder(folder: Folder): SerializedFolder;
|
|
90
|
+
export type SerializedFolderTree = SerializedFolder & {
|
|
91
|
+
folders: SerializedFolderTree[];
|
|
92
|
+
documents: SerializedDocumentTree[];
|
|
93
|
+
};
|
|
94
|
+
export type SerializedDocumentFolderTree = {
|
|
95
|
+
folders: SerializedFolderTree[];
|
|
96
|
+
documents: SerializedDocumentTree[];
|
|
97
|
+
};
|
|
98
|
+
export declare function buildFolderTree(folders: Folder[], documents: Document[]): SerializedDocumentFolderTree;
|
|
71
99
|
export declare function serializeEdge(edge: Edge): {
|
|
72
100
|
id: string;
|
|
73
101
|
project_id: string;
|
package/dist/serialize.js
CHANGED
|
@@ -36,7 +36,16 @@ export function serializeProjectDetail(project, summary) {
|
|
|
36
36
|
summary,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
export function
|
|
39
|
+
export function serializeTag(tag) {
|
|
40
|
+
return {
|
|
41
|
+
id: tag.id,
|
|
42
|
+
project_id: tag.projectId,
|
|
43
|
+
name: tag.name,
|
|
44
|
+
color: tag.color,
|
|
45
|
+
created_at: tag.createdAt.toISOString(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function serializeTask(task, tags) {
|
|
40
49
|
return {
|
|
41
50
|
id: task.id,
|
|
42
51
|
project_id: task.projectId,
|
|
@@ -49,6 +58,7 @@ export function serializeTask(task) {
|
|
|
49
58
|
due_date: task.dueDate?.toISOString() ?? null,
|
|
50
59
|
created_at: task.createdAt.toISOString(),
|
|
51
60
|
updated_at: task.updatedAt.toISOString(),
|
|
61
|
+
...(tags !== undefined ? { tags: tags.map(serializeTag) } : {}),
|
|
52
62
|
};
|
|
53
63
|
}
|
|
54
64
|
export function serializeDocument(document) {
|
|
@@ -59,6 +69,7 @@ export function serializeDocument(document) {
|
|
|
59
69
|
body: document.body,
|
|
60
70
|
status_line: document.statusLine,
|
|
61
71
|
parent_id: document.parentId,
|
|
72
|
+
folder_id: document.folderId,
|
|
62
73
|
linked_task_id: document.linkedTaskId,
|
|
63
74
|
created_at: document.createdAt.toISOString(),
|
|
64
75
|
updated_at: document.updatedAt.toISOString(),
|
|
@@ -109,6 +120,60 @@ export function buildDocumentTree(documents) {
|
|
|
109
120
|
}
|
|
110
121
|
return roots;
|
|
111
122
|
}
|
|
123
|
+
export function serializeFolder(folder) {
|
|
124
|
+
return {
|
|
125
|
+
id: folder.id,
|
|
126
|
+
project_id: folder.projectId,
|
|
127
|
+
name: folder.name,
|
|
128
|
+
parent_folder_id: folder.parentFolderId,
|
|
129
|
+
created_at: folder.createdAt.toISOString(),
|
|
130
|
+
updated_at: folder.updatedAt.toISOString(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export function buildFolderTree(folders, documents) {
|
|
134
|
+
const folderNodes = new Map();
|
|
135
|
+
for (const folder of folders) {
|
|
136
|
+
folderNodes.set(folder.id, { ...serializeFolder(folder), folders: [], documents: [] });
|
|
137
|
+
}
|
|
138
|
+
const rootFolders = [];
|
|
139
|
+
for (const folder of folders) {
|
|
140
|
+
const node = folderNodes.get(folder.id);
|
|
141
|
+
if (!node) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const parent = folder.parentFolderId === null ? undefined : folderNodes.get(folder.parentFolderId);
|
|
145
|
+
if (parent) {
|
|
146
|
+
parent.folders.push(node);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
rootFolders.push(node);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const documentsByFolder = new Map();
|
|
153
|
+
for (const document of documents) {
|
|
154
|
+
const key = document.folderId !== null && folderNodes.has(document.folderId) ? document.folderId : null;
|
|
155
|
+
const group = documentsByFolder.get(key);
|
|
156
|
+
if (group) {
|
|
157
|
+
group.push(document);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
documentsByFolder.set(key, [document]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const [folderId, group] of documentsByFolder) {
|
|
164
|
+
if (folderId === null) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const node = folderNodes.get(folderId);
|
|
168
|
+
if (node) {
|
|
169
|
+
node.documents = buildDocumentTree(group);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
folders: rootFolders,
|
|
174
|
+
documents: buildDocumentTree(documentsByFolder.get(null) ?? []),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
112
177
|
export function serializeEdge(edge) {
|
|
113
178
|
return {
|
|
114
179
|
id: edge.id,
|
package/dist/server.js
CHANGED
|
@@ -3,9 +3,11 @@ import { createAuthMiddleware } from './auth.js';
|
|
|
3
3
|
import { healthRouter } from './routes/health.js';
|
|
4
4
|
import { createProjectsRouter } from './routes/projects.js';
|
|
5
5
|
import { createTasksRouter } from './routes/tasks.js';
|
|
6
|
+
import { createTagsRouter } from './routes/tags.js';
|
|
6
7
|
import { createCanvasRouter } from './routes/canvas.js';
|
|
7
8
|
import { createCommentsRouter } from './routes/comments.js';
|
|
8
9
|
import { createDocumentsRouter } from './routes/documents.js';
|
|
10
|
+
import { createFoldersRouter } from './routes/folders.js';
|
|
9
11
|
import { createNotesRouter } from './routes/notes.js';
|
|
10
12
|
import { createEventsRouter } from './routes/events.js';
|
|
11
13
|
import { createTokensRouter } from './routes/tokens.js';
|
|
@@ -14,7 +16,7 @@ import { mountStatic } from './static.js';
|
|
|
14
16
|
import { createServices } from './services/index.js';
|
|
15
17
|
export function createApp(deps) {
|
|
16
18
|
const services = deps.services ?? createServices({ db: deps.db, eventBus: deps.eventBus });
|
|
17
|
-
const { eventBus, projectService, taskService, canvasService, documentService, noteService, commentService, agentRunService, tokenService, } = services;
|
|
19
|
+
const { eventBus, projectService, taskService, tagService, canvasService, documentService, folderService, noteService, commentService, agentRunService, tokenService, } = services;
|
|
18
20
|
const app = new Hono();
|
|
19
21
|
if (deps.authPassword !== undefined && deps.authPassword.length > 0) {
|
|
20
22
|
app.use('*', createAuthMiddleware(deps.authPassword));
|
|
@@ -22,8 +24,10 @@ export function createApp(deps) {
|
|
|
22
24
|
app.route('/api/v1', healthRouter);
|
|
23
25
|
app.route('/api/v1', createProjectsRouter(projectService, taskService));
|
|
24
26
|
app.route('/api/v1', createTasksRouter(taskService));
|
|
27
|
+
app.route('/api/v1', createTagsRouter(tagService));
|
|
25
28
|
app.route('/api/v1', createCanvasRouter(canvasService));
|
|
26
29
|
app.route('/api/v1', createDocumentsRouter(documentService));
|
|
30
|
+
app.route('/api/v1', createFoldersRouter(folderService));
|
|
27
31
|
app.route('/api/v1', createNotesRouter(noteService));
|
|
28
32
|
app.route('/api/v1', createCommentsRouter(commentService));
|
|
29
33
|
app.route('/api/v1', createTokensRouter(tokenService));
|
|
@@ -30,6 +30,7 @@ export declare class InvalidCanvasError extends Error {
|
|
|
30
30
|
export declare function createCanvasService(deps: CanvasServiceDeps): {
|
|
31
31
|
get(projectId: string): {
|
|
32
32
|
nodes: {
|
|
33
|
+
tags?: import("../serialize.js").SerializedTag[] | undefined;
|
|
33
34
|
id: string;
|
|
34
35
|
project_id: string;
|
|
35
36
|
label: string;
|
|
@@ -71,6 +72,7 @@ export declare function createCanvasService(deps: CanvasServiceDeps): {
|
|
|
71
72
|
} | undefined;
|
|
72
73
|
putLayout(projectId: string, payload: PutCanvasLayoutInput): {
|
|
73
74
|
nodes: {
|
|
75
|
+
tags?: import("../serialize.js").SerializedTag[] | undefined;
|
|
74
76
|
id: string;
|
|
75
77
|
project_id: string;
|
|
76
78
|
label: string;
|
package/dist/services/canvas.js
CHANGED
|
@@ -21,7 +21,7 @@ export function createCanvasService(deps) {
|
|
|
21
21
|
const tasks = listTasks(db, projectId);
|
|
22
22
|
const edgeRows = listEdges(db, projectId);
|
|
23
23
|
return {
|
|
24
|
-
nodes: tasks.map(serializeTask),
|
|
24
|
+
nodes: tasks.map((task) => serializeTask(task)),
|
|
25
25
|
edges: edgeRows.map(serializeEdge),
|
|
26
26
|
layout: serializeLayout(project.canvasLayout),
|
|
27
27
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Db } from '@plandesk/db';
|
|
2
|
-
import { type PaginationParams, type SerializedDocument, type SerializedDocumentTree } from '../serialize.js';
|
|
2
|
+
import { type PaginationParams, type SerializedDocument, type SerializedDocumentFolderTree, type SerializedDocumentTree } from '../serialize.js';
|
|
3
3
|
import type { EventBus } from '../events.js';
|
|
4
4
|
export type DocumentServiceDeps = {
|
|
5
5
|
db: Db;
|
|
@@ -10,6 +10,7 @@ export type CreateDocumentInput = {
|
|
|
10
10
|
body?: string | null;
|
|
11
11
|
statusLine?: string | null;
|
|
12
12
|
parentId?: string | null;
|
|
13
|
+
folderId?: string | null;
|
|
13
14
|
linkedTaskId?: string | null;
|
|
14
15
|
};
|
|
15
16
|
export type UpdateDocumentInput = {
|
|
@@ -17,6 +18,7 @@ export type UpdateDocumentInput = {
|
|
|
17
18
|
body?: string | null;
|
|
18
19
|
statusLine?: string | null;
|
|
19
20
|
parentId?: string | null;
|
|
21
|
+
folderId?: string | null;
|
|
20
22
|
linkedTaskId?: string | null;
|
|
21
23
|
};
|
|
22
24
|
export declare class InvalidDocumentError extends Error {
|
|
@@ -24,6 +26,8 @@ export declare class InvalidDocumentError extends Error {
|
|
|
24
26
|
}
|
|
25
27
|
export declare function createDocumentService(deps: DocumentServiceDeps): {
|
|
26
28
|
listTree(projectId: string, pagination?: PaginationParams): SerializedDocumentTree[] | undefined;
|
|
29
|
+
listFolderTree(projectId: string): SerializedDocumentFolderTree | undefined;
|
|
30
|
+
listByFolder(projectId: string, folderId: string): SerializedDocumentTree[] | undefined;
|
|
27
31
|
create(projectId: string, input: CreateDocumentInput): SerializedDocument | undefined;
|
|
28
32
|
get(id: string): SerializedDocument | undefined;
|
|
29
33
|
update(id: string, input: UpdateDocumentInput): SerializedDocument | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createDocument as dbCreateDocument, deleteCommentsByDocumentId, deleteDocument as dbDeleteDocument, detachDocumentChildren, getDocument as dbGetDocument, getDocumentByTask as dbGetDocumentByTask, getProject, getTask, listDocuments as dbListDocuments, updateDocument as dbUpdateDocument, } from '@plandesk/db';
|
|
2
|
-
import { buildDocumentTree, serializeDocument, } from '../serialize.js';
|
|
1
|
+
import { createDocument as dbCreateDocument, deleteCommentsByDocumentId, deleteDocument as dbDeleteDocument, detachDocumentChildren, getDocument as dbGetDocument, getDocumentByTask as dbGetDocumentByTask, getFolderByProjectAndId, getProject, getTask, listDocuments as dbListDocuments, listFolders as dbListFolders, updateDocument as dbUpdateDocument, } from '@plandesk/db';
|
|
2
|
+
import { buildDocumentTree, buildFolderTree, serializeDocument, } from '../serialize.js';
|
|
3
3
|
export class InvalidDocumentError extends Error {
|
|
4
4
|
constructor(message) {
|
|
5
5
|
super(message);
|
|
@@ -18,6 +18,11 @@ function assertParentInProject(db, projectId, parentId) {
|
|
|
18
18
|
throw new InvalidDocumentError('Parent document does not belong to project');
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
function assertFolderInProject(db, projectId, folderId) {
|
|
22
|
+
if (!getFolderByProjectAndId(db, projectId, folderId)) {
|
|
23
|
+
throw new InvalidDocumentError('Folder does not belong to project');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
21
26
|
export function createDocumentService(deps) {
|
|
22
27
|
const { db, eventBus } = deps;
|
|
23
28
|
return {
|
|
@@ -28,6 +33,23 @@ export function createDocumentService(deps) {
|
|
|
28
33
|
}
|
|
29
34
|
return buildDocumentTree(dbListDocuments(db, projectId, pagination));
|
|
30
35
|
},
|
|
36
|
+
listFolderTree(projectId) {
|
|
37
|
+
const project = getProject(db, projectId);
|
|
38
|
+
if (!project) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return buildFolderTree(dbListFolders(db, projectId), dbListDocuments(db, projectId));
|
|
42
|
+
},
|
|
43
|
+
listByFolder(projectId, folderId) {
|
|
44
|
+
const project = getProject(db, projectId);
|
|
45
|
+
if (!project) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (!getFolderByProjectAndId(db, projectId, folderId)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return buildDocumentTree(dbListDocuments(db, projectId, { folderId }));
|
|
52
|
+
},
|
|
31
53
|
create(projectId, input) {
|
|
32
54
|
const project = getProject(db, projectId);
|
|
33
55
|
if (!project) {
|
|
@@ -39,12 +61,16 @@ export function createDocumentService(deps) {
|
|
|
39
61
|
if (input.parentId !== undefined && input.parentId !== null) {
|
|
40
62
|
assertParentInProject(db, projectId, input.parentId);
|
|
41
63
|
}
|
|
64
|
+
if (input.folderId !== undefined && input.folderId !== null) {
|
|
65
|
+
assertFolderInProject(db, projectId, input.folderId);
|
|
66
|
+
}
|
|
42
67
|
const document = dbCreateDocument(db, {
|
|
43
68
|
projectId,
|
|
44
69
|
title: input.title,
|
|
45
70
|
body: input.body,
|
|
46
71
|
statusLine: input.statusLine,
|
|
47
72
|
parentId: input.parentId,
|
|
73
|
+
folderId: input.folderId,
|
|
48
74
|
linkedTaskId: input.linkedTaskId,
|
|
49
75
|
});
|
|
50
76
|
eventBus.emit({
|
|
@@ -75,6 +101,9 @@ export function createDocumentService(deps) {
|
|
|
75
101
|
}
|
|
76
102
|
assertParentInProject(db, existing.projectId, input.parentId);
|
|
77
103
|
}
|
|
104
|
+
if (input.folderId !== undefined && input.folderId !== null) {
|
|
105
|
+
assertFolderInProject(db, existing.projectId, input.folderId);
|
|
106
|
+
}
|
|
78
107
|
const document = dbUpdateDocument(db, id, input);
|
|
79
108
|
if (!document) {
|
|
80
109
|
return undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Db } from '@plandesk/db';
|
|
2
|
+
import { type SerializedFolder } from '../serialize.js';
|
|
3
|
+
import type { EventBus } from '../events.js';
|
|
4
|
+
export type FolderServiceDeps = {
|
|
5
|
+
db: Db;
|
|
6
|
+
eventBus: EventBus;
|
|
7
|
+
};
|
|
8
|
+
export type CreateFolderInput = {
|
|
9
|
+
name: string;
|
|
10
|
+
parentFolderId?: string | null;
|
|
11
|
+
};
|
|
12
|
+
export type UpdateFolderInput = {
|
|
13
|
+
name?: string;
|
|
14
|
+
parentFolderId?: string | null;
|
|
15
|
+
};
|
|
16
|
+
export declare class InvalidFolderError extends Error {
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
19
|
+
export declare function createFolderService(deps: FolderServiceDeps): {
|
|
20
|
+
list(projectId: string): SerializedFolder[] | undefined;
|
|
21
|
+
create(projectId: string, input: CreateFolderInput): SerializedFolder | undefined;
|
|
22
|
+
get(id: string): SerializedFolder | undefined;
|
|
23
|
+
update(id: string, input: UpdateFolderInput): SerializedFolder | undefined;
|
|
24
|
+
delete(id: string): boolean;
|
|
25
|
+
};
|
|
26
|
+
export type FolderService = ReturnType<typeof createFolderService>;
|
|
27
|
+
//# sourceMappingURL=folders.d.ts.map
|