agkan 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +1127 -0
- package/README.md +1140 -0
- package/bin/agkan +2 -0
- package/dist/cli/commands/block/add.d.ts +6 -0
- package/dist/cli/commands/block/add.d.ts.map +1 -0
- package/dist/cli/commands/block/add.js +135 -0
- package/dist/cli/commands/block/add.js.map +1 -0
- package/dist/cli/commands/block/list.d.ts +6 -0
- package/dist/cli/commands/block/list.d.ts.map +1 -0
- package/dist/cli/commands/block/list.js +120 -0
- package/dist/cli/commands/block/list.js.map +1 -0
- package/dist/cli/commands/block/remove.d.ts +6 -0
- package/dist/cli/commands/block/remove.d.ts.map +1 -0
- package/dist/cli/commands/block/remove.js +117 -0
- package/dist/cli/commands/block/remove.js.map +1 -0
- package/dist/cli/commands/meta/delete.d.ts +6 -0
- package/dist/cli/commands/meta/delete.d.ts.map +1 -0
- package/dist/cli/commands/meta/delete.js +67 -0
- package/dist/cli/commands/meta/delete.js.map +1 -0
- package/dist/cli/commands/meta/get.d.ts +6 -0
- package/dist/cli/commands/meta/get.d.ts.map +1 -0
- package/dist/cli/commands/meta/get.js +71 -0
- package/dist/cli/commands/meta/get.js.map +1 -0
- package/dist/cli/commands/meta/list.d.ts +6 -0
- package/dist/cli/commands/meta/list.d.ts.map +1 -0
- package/dist/cli/commands/meta/list.js +69 -0
- package/dist/cli/commands/meta/list.js.map +1 -0
- package/dist/cli/commands/meta/set.d.ts +6 -0
- package/dist/cli/commands/meta/set.d.ts.map +1 -0
- package/dist/cli/commands/meta/set.js +85 -0
- package/dist/cli/commands/meta/set.js.map +1 -0
- package/dist/cli/commands/tag/add.d.ts +6 -0
- package/dist/cli/commands/tag/add.d.ts.map +1 -0
- package/dist/cli/commands/tag/add.js +99 -0
- package/dist/cli/commands/tag/add.js.map +1 -0
- package/dist/cli/commands/tag/attach.d.ts +6 -0
- package/dist/cli/commands/tag/attach.d.ts.map +1 -0
- package/dist/cli/commands/tag/attach.js +119 -0
- package/dist/cli/commands/tag/attach.js.map +1 -0
- package/dist/cli/commands/tag/delete.d.ts +6 -0
- package/dist/cli/commands/tag/delete.d.ts.map +1 -0
- package/dist/cli/commands/tag/delete.js +93 -0
- package/dist/cli/commands/tag/delete.js.map +1 -0
- package/dist/cli/commands/tag/detach.d.ts +6 -0
- package/dist/cli/commands/tag/detach.d.ts.map +1 -0
- package/dist/cli/commands/tag/detach.js +118 -0
- package/dist/cli/commands/tag/detach.js.map +1 -0
- package/dist/cli/commands/tag/list.d.ts +6 -0
- package/dist/cli/commands/tag/list.d.ts.map +1 -0
- package/dist/cli/commands/tag/list.js +80 -0
- package/dist/cli/commands/tag/list.js.map +1 -0
- package/dist/cli/commands/tag/show.d.ts +6 -0
- package/dist/cli/commands/tag/show.d.ts.map +1 -0
- package/dist/cli/commands/tag/show.js +88 -0
- package/dist/cli/commands/tag/show.js.map +1 -0
- package/dist/cli/commands/task/add-helpers.d.ts +17 -0
- package/dist/cli/commands/task/add-helpers.d.ts.map +1 -0
- package/dist/cli/commands/task/add-helpers.js +122 -0
- package/dist/cli/commands/task/add-helpers.js.map +1 -0
- package/dist/cli/commands/task/add.d.ts +6 -0
- package/dist/cli/commands/task/add.d.ts.map +1 -0
- package/dist/cli/commands/task/add.js +140 -0
- package/dist/cli/commands/task/add.js.map +1 -0
- package/dist/cli/commands/task/count.d.ts +6 -0
- package/dist/cli/commands/task/count.d.ts.map +1 -0
- package/dist/cli/commands/task/count.js +97 -0
- package/dist/cli/commands/task/count.js.map +1 -0
- package/dist/cli/commands/task/delete.d.ts +6 -0
- package/dist/cli/commands/task/delete.d.ts.map +1 -0
- package/dist/cli/commands/task/delete.js +59 -0
- package/dist/cli/commands/task/delete.js.map +1 -0
- package/dist/cli/commands/task/find.d.ts +7 -0
- package/dist/cli/commands/task/find.d.ts.map +1 -0
- package/dist/cli/commands/task/find.js +118 -0
- package/dist/cli/commands/task/find.js.map +1 -0
- package/dist/cli/commands/task/get.d.ts +6 -0
- package/dist/cli/commands/task/get.d.ts.map +1 -0
- package/dist/cli/commands/task/get.js +196 -0
- package/dist/cli/commands/task/get.js.map +1 -0
- package/dist/cli/commands/task/list.d.ts +6 -0
- package/dist/cli/commands/task/list.d.ts.map +1 -0
- package/dist/cli/commands/task/list.js +301 -0
- package/dist/cli/commands/task/list.js.map +1 -0
- package/dist/cli/commands/task/update-parent.d.ts +6 -0
- package/dist/cli/commands/task/update-parent.d.ts.map +1 -0
- package/dist/cli/commands/task/update-parent.js +123 -0
- package/dist/cli/commands/task/update-parent.js.map +1 -0
- package/dist/cli/commands/task/update.d.ts +6 -0
- package/dist/cli/commands/task/update.d.ts.map +1 -0
- package/dist/cli/commands/task/update.js +96 -0
- package/dist/cli/commands/task/update.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +68 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/array-utils.d.ts +15 -0
- package/dist/cli/utils/array-utils.d.ts.map +1 -0
- package/dist/cli/utils/array-utils.js +20 -0
- package/dist/cli/utils/array-utils.js.map +1 -0
- package/dist/cli/utils/error-handler.d.ts +35 -0
- package/dist/cli/utils/error-handler.d.ts.map +1 -0
- package/dist/cli/utils/error-handler.js +84 -0
- package/dist/cli/utils/error-handler.js.map +1 -0
- package/dist/cli/utils/output-formatter.d.ts +34 -0
- package/dist/cli/utils/output-formatter.d.ts.map +1 -0
- package/dist/cli/utils/output-formatter.js +44 -0
- package/dist/cli/utils/output-formatter.js.map +1 -0
- package/dist/cli/utils/response-formatter.d.ts +19 -0
- package/dist/cli/utils/response-formatter.d.ts.map +1 -0
- package/dist/cli/utils/response-formatter.js +43 -0
- package/dist/cli/utils/response-formatter.js.map +1 -0
- package/dist/cli/utils/validators.d.ts +23 -0
- package/dist/cli/utils/validators.d.ts.map +1 -0
- package/dist/cli/utils/validators.js +47 -0
- package/dist/cli/utils/validators.js.map +1 -0
- package/dist/db/config.d.ts +27 -0
- package/dist/db/config.d.ts.map +1 -0
- package/dist/db/config.js +116 -0
- package/dist/db/config.js.map +1 -0
- package/dist/db/connection.d.ts +24 -0
- package/dist/db/connection.d.ts.map +1 -0
- package/dist/db/connection.js +62 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/reset.d.ts +8 -0
- package/dist/db/reset.d.ts.map +1 -0
- package/dist/db/reset.js +33 -0
- package/dist/db/reset.js.map +1 -0
- package/dist/db/schema.d.ts +6 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +134 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/models/Attachment.d.ts +25 -0
- package/dist/models/Attachment.d.ts.map +1 -0
- package/dist/models/Attachment.js +7 -0
- package/dist/models/Attachment.js.map +1 -0
- package/dist/models/Tag.d.ts +24 -0
- package/dist/models/Tag.d.ts.map +1 -0
- package/dist/models/Tag.js +3 -0
- package/dist/models/Tag.js.map +1 -0
- package/dist/models/Task.d.ts +55 -0
- package/dist/models/Task.d.ts.map +1 -0
- package/dist/models/Task.js +7 -0
- package/dist/models/Task.js.map +1 -0
- package/dist/models/TaskBlock.d.ts +11 -0
- package/dist/models/TaskBlock.d.ts.map +1 -0
- package/dist/models/TaskBlock.js +3 -0
- package/dist/models/TaskBlock.js.map +1 -0
- package/dist/models/TaskMetadata.d.ts +30 -0
- package/dist/models/TaskMetadata.d.ts.map +1 -0
- package/dist/models/TaskMetadata.js +3 -0
- package/dist/models/TaskMetadata.js.map +1 -0
- package/dist/models/TaskTag.d.ts +11 -0
- package/dist/models/TaskTag.d.ts.map +1 -0
- package/dist/models/TaskTag.js +3 -0
- package/dist/models/TaskTag.js.map +1 -0
- package/dist/models/index.d.ts +10 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +7 -0
- package/dist/models/index.js.map +1 -0
- package/dist/services/AttachmentService.d.ts +62 -0
- package/dist/services/AttachmentService.d.ts.map +1 -0
- package/dist/services/AttachmentService.js +95 -0
- package/dist/services/AttachmentService.js.map +1 -0
- package/dist/services/FileService.d.ts +31 -0
- package/dist/services/FileService.d.ts.map +1 -0
- package/dist/services/FileService.js +77 -0
- package/dist/services/FileService.js.map +1 -0
- package/dist/services/MetadataService.d.ts +49 -0
- package/dist/services/MetadataService.d.ts.map +1 -0
- package/dist/services/MetadataService.js +126 -0
- package/dist/services/MetadataService.js.map +1 -0
- package/dist/services/TagService.d.ts +49 -0
- package/dist/services/TagService.d.ts.map +1 -0
- package/dist/services/TagService.js +127 -0
- package/dist/services/TagService.js.map +1 -0
- package/dist/services/TaskBlockService.d.ts +49 -0
- package/dist/services/TaskBlockService.d.ts.map +1 -0
- package/dist/services/TaskBlockService.js +118 -0
- package/dist/services/TaskBlockService.js.map +1 -0
- package/dist/services/TaskService.d.ts +89 -0
- package/dist/services/TaskService.d.ts.map +1 -0
- package/dist/services/TaskService.js +285 -0
- package/dist/services/TaskService.js.map +1 -0
- package/dist/services/TaskTagService.d.ts +66 -0
- package/dist/services/TaskTagService.d.ts.map +1 -0
- package/dist/services/TaskTagService.js +171 -0
- package/dist/services/TaskTagService.js.map +1 -0
- package/dist/services/index.d.ts +11 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +20 -0
- package/dist/services/index.js.map +1 -0
- package/dist/utils/cycle-detector.d.ts +14 -0
- package/dist/utils/cycle-detector.d.ts.map +1 -0
- package/dist/utils/cycle-detector.js +32 -0
- package/dist/utils/cycle-detector.js.map +1 -0
- package/dist/utils/format.d.ts +13 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +29 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/input-validators.d.ts +32 -0
- package/dist/utils/input-validators.d.ts.map +1 -0
- package/dist/utils/input-validators.js +108 -0
- package/dist/utils/input-validators.js.map +1 -0
- package/dist/utils/security.d.ts +11 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +29 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TaskBlockService = void 0;
|
|
4
|
+
const connection_1 = require("../db/connection");
|
|
5
|
+
const TaskService_1 = require("./TaskService");
|
|
6
|
+
/**
|
|
7
|
+
* Task Block Service
|
|
8
|
+
* Manages blocking relationships between tasks
|
|
9
|
+
*/
|
|
10
|
+
class TaskBlockService {
|
|
11
|
+
constructor(db, taskService) {
|
|
12
|
+
this.db = db || (0, connection_1.getDatabase)();
|
|
13
|
+
this.taskService = taskService || new TaskService_1.TaskService(this.db);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Add a blocking relationship between tasks
|
|
17
|
+
* @param input - Creation input for the blocking relationship
|
|
18
|
+
* @returns Created blocking relationship
|
|
19
|
+
* @throws Error if tasks do not exist, self-reference is detected, or circular reference would be created
|
|
20
|
+
*/
|
|
21
|
+
addBlock(input) {
|
|
22
|
+
const db = this.db;
|
|
23
|
+
// Check for self-reference: prevent a task from blocking itself
|
|
24
|
+
if (input.blocker_task_id === input.blocked_task_id) {
|
|
25
|
+
throw new Error('Task cannot block itself');
|
|
26
|
+
}
|
|
27
|
+
// Verify both tasks exist in the database
|
|
28
|
+
const blockerTask = this.taskService.getTask(input.blocker_task_id);
|
|
29
|
+
if (!blockerTask) {
|
|
30
|
+
throw new Error(`Blocker task with id ${input.blocker_task_id} does not exist`);
|
|
31
|
+
}
|
|
32
|
+
const blockedTask = this.taskService.getTask(input.blocked_task_id);
|
|
33
|
+
if (!blockedTask) {
|
|
34
|
+
throw new Error(`Blocked task with id ${input.blocked_task_id} does not exist`);
|
|
35
|
+
}
|
|
36
|
+
// Check for circular reference: ensure new blocking relationship doesn't create a cycle
|
|
37
|
+
if (this.wouldCreateBlockCycle(input.blocker_task_id, input.blocked_task_id)) {
|
|
38
|
+
throw new Error(`Cannot create block relationship: would create circular dependency between tasks ${input.blocker_task_id} and ${input.blocked_task_id}`);
|
|
39
|
+
}
|
|
40
|
+
const now = new Date().toISOString();
|
|
41
|
+
const stmt = db.prepare(`
|
|
42
|
+
INSERT INTO task_blocks (blocker_task_id, blocked_task_id, created_at)
|
|
43
|
+
VALUES (?, ?, ?)
|
|
44
|
+
`);
|
|
45
|
+
const result = stmt.run(input.blocker_task_id, input.blocked_task_id, now);
|
|
46
|
+
const getStmt = db.prepare('SELECT * FROM task_blocks WHERE id = ?');
|
|
47
|
+
return getStmt.get(result.lastInsertRowid);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Remove a blocking relationship between tasks
|
|
51
|
+
* @param blockerId - ID of the blocking task
|
|
52
|
+
* @param blockedId - ID of the blocked task
|
|
53
|
+
* @returns True if removal was successful, false if relationship was not found
|
|
54
|
+
*/
|
|
55
|
+
removeBlock(blockerId, blockedId) {
|
|
56
|
+
const db = this.db;
|
|
57
|
+
const stmt = db.prepare(`
|
|
58
|
+
DELETE FROM task_blocks
|
|
59
|
+
WHERE blocker_task_id = ? AND blocked_task_id = ?
|
|
60
|
+
`);
|
|
61
|
+
const result = stmt.run(blockerId, blockedId);
|
|
62
|
+
return result.changes > 0;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get IDs of tasks blocked by the specified task
|
|
66
|
+
* Returns a list of IDs for tasks that are blocked by the given blocker task
|
|
67
|
+
* @param blockerId - ID of the blocking task
|
|
68
|
+
* @returns Array of IDs for tasks blocked by this task
|
|
69
|
+
*/
|
|
70
|
+
getBlockedTaskIds(blockerId) {
|
|
71
|
+
const db = this.db;
|
|
72
|
+
const stmt = db.prepare(`
|
|
73
|
+
SELECT blocked_task_id FROM task_blocks
|
|
74
|
+
WHERE blocker_task_id = ?
|
|
75
|
+
`);
|
|
76
|
+
const results = stmt.all(blockerId);
|
|
77
|
+
return results.map((row) => row.blocked_task_id);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get IDs of tasks blocking the specified task
|
|
81
|
+
* Returns a list of IDs for tasks that block the given task
|
|
82
|
+
* @param blockedId - ID of the blocked task
|
|
83
|
+
* @returns Array of IDs for blocking tasks
|
|
84
|
+
*/
|
|
85
|
+
getBlockerTaskIds(blockedId) {
|
|
86
|
+
const db = this.db;
|
|
87
|
+
const stmt = db.prepare(`
|
|
88
|
+
SELECT blocker_task_id FROM task_blocks
|
|
89
|
+
WHERE blocked_task_id = ?
|
|
90
|
+
`);
|
|
91
|
+
const results = stmt.all(blockedId);
|
|
92
|
+
return results.map((row) => row.blocker_task_id);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check for circular references in blocking relationships (private method)
|
|
96
|
+
* Detects whether adding a new blocking relationship would create a cycle using BFS algorithm
|
|
97
|
+
* @param blockerId - ID of the blocking task
|
|
98
|
+
* @param blockedId - ID of the blocked task
|
|
99
|
+
* @returns True if a circular reference would be created, false otherwise
|
|
100
|
+
*/
|
|
101
|
+
wouldCreateBlockCycle(blockerId, blockedId) {
|
|
102
|
+
const visited = new Set();
|
|
103
|
+
const queue = [blockedId];
|
|
104
|
+
while (queue.length > 0) {
|
|
105
|
+
const current = queue.shift();
|
|
106
|
+
if (visited.has(current))
|
|
107
|
+
continue;
|
|
108
|
+
visited.add(current);
|
|
109
|
+
if (current === blockerId)
|
|
110
|
+
return true;
|
|
111
|
+
const blockedByCurrentIds = this.getBlockedTaskIds(current);
|
|
112
|
+
queue.push(...blockedByCurrentIds);
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.TaskBlockService = TaskBlockService;
|
|
118
|
+
//# sourceMappingURL=TaskBlockService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskBlockService.js","sourceRoot":"","sources":["../../src/services/TaskBlockService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Task, CreateTaskInput, UpdateTaskInput, TaskStatus } from '../models';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
/**
|
|
4
|
+
* Task Service
|
|
5
|
+
* Provides CRUD operations for tasks
|
|
6
|
+
*/
|
|
7
|
+
export declare class TaskService {
|
|
8
|
+
private db;
|
|
9
|
+
constructor(db?: Database.Database);
|
|
10
|
+
/**
|
|
11
|
+
* Create a new task
|
|
12
|
+
* @param input - Task creation input
|
|
13
|
+
* @returns Created task
|
|
14
|
+
*/
|
|
15
|
+
createTask(input: CreateTaskInput): Task;
|
|
16
|
+
/**
|
|
17
|
+
* Get task by ID
|
|
18
|
+
* @param id - Task ID
|
|
19
|
+
* @returns Task, or null if not found
|
|
20
|
+
*/
|
|
21
|
+
getTask(id: number): Task | null;
|
|
22
|
+
/**
|
|
23
|
+
* Get task list
|
|
24
|
+
* @param filters - Filter criteria (status, author, tagIds)
|
|
25
|
+
* @returns Array of tasks
|
|
26
|
+
*/
|
|
27
|
+
listTasks(filters?: {
|
|
28
|
+
status?: TaskStatus;
|
|
29
|
+
author?: string;
|
|
30
|
+
tagIds?: number[];
|
|
31
|
+
}): Task[];
|
|
32
|
+
/**
|
|
33
|
+
* Build dynamic UPDATE query for task fields
|
|
34
|
+
* @param input - Update content
|
|
35
|
+
* @param id - Task ID (appended as last param for WHERE clause)
|
|
36
|
+
* @returns Object with sql string and params array
|
|
37
|
+
*/
|
|
38
|
+
private buildUpdateQuery;
|
|
39
|
+
/**
|
|
40
|
+
* Update task
|
|
41
|
+
* @param id - Task ID
|
|
42
|
+
* @param input - Update content
|
|
43
|
+
* @returns Updated task, or null if not found
|
|
44
|
+
*/
|
|
45
|
+
updateTask(id: number, input: UpdateTaskInput): Task | null;
|
|
46
|
+
/**
|
|
47
|
+
* Delete task
|
|
48
|
+
* @param id - Task ID
|
|
49
|
+
* @returns true if deletion succeeded, false if task not found
|
|
50
|
+
*/
|
|
51
|
+
deleteTask(id: number): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Get task count by status
|
|
54
|
+
* @returns Map of task count for each status
|
|
55
|
+
*/
|
|
56
|
+
getTaskCountByStatus(): Record<TaskStatus, number>;
|
|
57
|
+
/**
|
|
58
|
+
* Search tasks
|
|
59
|
+
* @param keyword - Search keyword (LIKE search on title and body)
|
|
60
|
+
* @param includeAll - If true, include done/closed tasks in search (default: false)
|
|
61
|
+
* @returns Array of matched tasks
|
|
62
|
+
*/
|
|
63
|
+
searchTasks(keyword: string, includeAll?: boolean): Task[];
|
|
64
|
+
/**
|
|
65
|
+
* Get child tasks (direct children only)
|
|
66
|
+
* @param parentId - Parent task ID
|
|
67
|
+
* @returns Array of child tasks
|
|
68
|
+
*/
|
|
69
|
+
getChildTasks(parentId: number): Task[];
|
|
70
|
+
/**
|
|
71
|
+
* Get parent task
|
|
72
|
+
* @param taskId - Task ID
|
|
73
|
+
* @returns Parent task, or null if no parent exists
|
|
74
|
+
*/
|
|
75
|
+
getParentTask(taskId: number): Task | null;
|
|
76
|
+
/**
|
|
77
|
+
* Get descendant tasks iteratively (all descendants)
|
|
78
|
+
* @param parentId - Parent task ID
|
|
79
|
+
* @returns Array of all descendant tasks
|
|
80
|
+
*/
|
|
81
|
+
getDescendantTasks(parentId: number): Task[];
|
|
82
|
+
/**
|
|
83
|
+
* Get root task (top-level parent)
|
|
84
|
+
* @param taskId - Task ID
|
|
85
|
+
* @returns Root task, or null if task not found
|
|
86
|
+
*/
|
|
87
|
+
getRootTask(taskId: number): Task | null;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=TaskService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskService.d.ts","sourceRoot":"","sources":["../../src/services/TaskService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAI/E,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAoB;gBAElB,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ;IAIlC;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAqCxC;;;;OAIG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAShC;;;;OAIG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,UAAU,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,EAAE;IAkCxF;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAqCxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI;IA8B3D;;;;OAIG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAc/B;;;OAGG;IACH,oBAAoB,IAAI,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC;IA6BlD;;;;;OAKG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,OAAe,GAAG,IAAI,EAAE;IAiBjE;;;;OAIG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE;IAMvC;;;;OAIG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAQ1C;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE;IAwB5C;;;;OAIG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;CAuBzC"}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TaskService = void 0;
|
|
4
|
+
const connection_1 = require("../db/connection");
|
|
5
|
+
const input_validators_1 = require("../utils/input-validators");
|
|
6
|
+
const cycle_detector_1 = require("../utils/cycle-detector");
|
|
7
|
+
/**
|
|
8
|
+
* Task Service
|
|
9
|
+
* Provides CRUD operations for tasks
|
|
10
|
+
*/
|
|
11
|
+
class TaskService {
|
|
12
|
+
constructor(db) {
|
|
13
|
+
this.db = db || (0, connection_1.getDatabase)();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a new task
|
|
17
|
+
* @param input - Task creation input
|
|
18
|
+
* @returns Created task
|
|
19
|
+
*/
|
|
20
|
+
createTask(input) {
|
|
21
|
+
const db = this.db;
|
|
22
|
+
const now = new Date().toISOString();
|
|
23
|
+
const status = input.status || 'backlog';
|
|
24
|
+
// Validate input fields
|
|
25
|
+
const validationErrors = (0, input_validators_1.validateTaskInput)(input);
|
|
26
|
+
if (validationErrors.length > 0) {
|
|
27
|
+
throw new Error(validationErrors[0].message);
|
|
28
|
+
}
|
|
29
|
+
// Validate parent_id: check if parent task exists
|
|
30
|
+
if (input.parent_id !== undefined && input.parent_id !== null) {
|
|
31
|
+
const parentTask = this.getTask(input.parent_id);
|
|
32
|
+
if (!parentTask) {
|
|
33
|
+
throw new Error(`Parent task with id ${input.parent_id} does not exist`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const stmt = db.prepare(`
|
|
37
|
+
INSERT INTO tasks (title, body, author, status, parent_id, created_at, updated_at)
|
|
38
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
39
|
+
`);
|
|
40
|
+
const result = stmt.run(input.title, input.body || null, input.author || null, status, input.parent_id !== undefined ? input.parent_id : null, now, now);
|
|
41
|
+
return this.getTask(result.lastInsertRowid);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get task by ID
|
|
45
|
+
* @param id - Task ID
|
|
46
|
+
* @returns Task, or null if not found
|
|
47
|
+
*/
|
|
48
|
+
getTask(id) {
|
|
49
|
+
const db = this.db;
|
|
50
|
+
const stmt = db.prepare('SELECT * FROM tasks WHERE id = ?');
|
|
51
|
+
const task = stmt.get(id);
|
|
52
|
+
return task || null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get task list
|
|
56
|
+
* @param filters - Filter criteria (status, author, tagIds)
|
|
57
|
+
* @returns Array of tasks
|
|
58
|
+
*/
|
|
59
|
+
listTasks(filters) {
|
|
60
|
+
const db = this.db;
|
|
61
|
+
let query;
|
|
62
|
+
const params = [];
|
|
63
|
+
// Use JOIN when tag filter is specified
|
|
64
|
+
if (filters?.tagIds && filters.tagIds.length > 0) {
|
|
65
|
+
query = 'SELECT DISTINCT tasks.* FROM tasks INNER JOIN task_tags ON tasks.id = task_tags.task_id WHERE 1=1';
|
|
66
|
+
// Add IN clause for tag IDs
|
|
67
|
+
const placeholders = filters.tagIds.map(() => '?').join(', ');
|
|
68
|
+
query += ` AND task_tags.tag_id IN (${placeholders})`;
|
|
69
|
+
params.push(...filters.tagIds);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
query = 'SELECT * FROM tasks WHERE 1=1';
|
|
73
|
+
}
|
|
74
|
+
if (filters?.status) {
|
|
75
|
+
query += ' AND status = ?';
|
|
76
|
+
params.push(filters.status);
|
|
77
|
+
}
|
|
78
|
+
if (filters?.author) {
|
|
79
|
+
query += ' AND author = ?';
|
|
80
|
+
params.push(filters.author);
|
|
81
|
+
}
|
|
82
|
+
query += ' ORDER BY created_at DESC';
|
|
83
|
+
const stmt = db.prepare(query);
|
|
84
|
+
return stmt.all(...params);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build dynamic UPDATE query for task fields
|
|
88
|
+
* @param input - Update content
|
|
89
|
+
* @param id - Task ID (appended as last param for WHERE clause)
|
|
90
|
+
* @returns Object with sql string and params array
|
|
91
|
+
*/
|
|
92
|
+
buildUpdateQuery(input, id) {
|
|
93
|
+
const updates = [];
|
|
94
|
+
const params = [];
|
|
95
|
+
if (input.title !== undefined) {
|
|
96
|
+
updates.push('title = ?');
|
|
97
|
+
params.push(input.title);
|
|
98
|
+
}
|
|
99
|
+
if (input.body !== undefined) {
|
|
100
|
+
updates.push('body = ?');
|
|
101
|
+
params.push(input.body);
|
|
102
|
+
}
|
|
103
|
+
if (input.author !== undefined) {
|
|
104
|
+
updates.push('author = ?');
|
|
105
|
+
params.push(input.author);
|
|
106
|
+
}
|
|
107
|
+
if (input.status !== undefined) {
|
|
108
|
+
updates.push('status = ?');
|
|
109
|
+
params.push(input.status);
|
|
110
|
+
}
|
|
111
|
+
if (input.parent_id !== undefined) {
|
|
112
|
+
updates.push('parent_id = ?');
|
|
113
|
+
params.push(input.parent_id);
|
|
114
|
+
}
|
|
115
|
+
updates.push('updated_at = ?');
|
|
116
|
+
params.push(new Date().toISOString());
|
|
117
|
+
params.push(id);
|
|
118
|
+
const sql = `UPDATE tasks SET ${updates.join(', ')} WHERE id = ?`;
|
|
119
|
+
return { sql, params };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Update task
|
|
123
|
+
* @param id - Task ID
|
|
124
|
+
* @param input - Update content
|
|
125
|
+
* @returns Updated task, or null if not found
|
|
126
|
+
*/
|
|
127
|
+
updateTask(id, input) {
|
|
128
|
+
const task = this.getTask(id);
|
|
129
|
+
if (!task) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const validationErrors = (0, input_validators_1.validateTaskUpdateInput)(input);
|
|
133
|
+
if (validationErrors.length > 0) {
|
|
134
|
+
throw new Error(validationErrors[0].message);
|
|
135
|
+
}
|
|
136
|
+
if (input.parent_id !== undefined) {
|
|
137
|
+
if ((0, cycle_detector_1.wouldCreateCycle)(id, input.parent_id, (parentId) => this.getTask(parentId)?.parent_id ?? null)) {
|
|
138
|
+
throw new Error(`Cannot set parent_id to ${input.parent_id}: would create circular reference`);
|
|
139
|
+
}
|
|
140
|
+
if (input.parent_id !== null) {
|
|
141
|
+
const parentTask = this.getTask(input.parent_id);
|
|
142
|
+
if (!parentTask) {
|
|
143
|
+
throw new Error(`Parent task with id ${input.parent_id} does not exist`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const { sql, params } = this.buildUpdateQuery(input, id);
|
|
148
|
+
this.db.prepare(sql).run(...params);
|
|
149
|
+
return this.getTask(id);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Delete task
|
|
153
|
+
* @param id - Task ID
|
|
154
|
+
* @returns true if deletion succeeded, false if task not found
|
|
155
|
+
*/
|
|
156
|
+
deleteTask(id) {
|
|
157
|
+
const db = this.db;
|
|
158
|
+
const task = this.getTask(id);
|
|
159
|
+
if (!task) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const stmt = db.prepare('DELETE FROM tasks WHERE id = ?');
|
|
163
|
+
stmt.run(id);
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get task count by status
|
|
168
|
+
* @returns Map of task count for each status
|
|
169
|
+
*/
|
|
170
|
+
getTaskCountByStatus() {
|
|
171
|
+
const db = this.db;
|
|
172
|
+
const stmt = db.prepare(`
|
|
173
|
+
SELECT status, COUNT(*) as count
|
|
174
|
+
FROM tasks
|
|
175
|
+
GROUP BY status
|
|
176
|
+
`);
|
|
177
|
+
const results = stmt.all();
|
|
178
|
+
// Initialize all statuses with 0
|
|
179
|
+
const countMap = {
|
|
180
|
+
backlog: 0,
|
|
181
|
+
ready: 0,
|
|
182
|
+
in_progress: 0,
|
|
183
|
+
review: 0,
|
|
184
|
+
done: 0,
|
|
185
|
+
closed: 0,
|
|
186
|
+
};
|
|
187
|
+
// Update map with query results
|
|
188
|
+
results.forEach((row) => {
|
|
189
|
+
countMap[row.status] = row.count;
|
|
190
|
+
});
|
|
191
|
+
return countMap;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Search tasks
|
|
195
|
+
* @param keyword - Search keyword (LIKE search on title and body)
|
|
196
|
+
* @param includeAll - If true, include done/closed tasks in search (default: false)
|
|
197
|
+
* @returns Array of matched tasks
|
|
198
|
+
*/
|
|
199
|
+
searchTasks(keyword, includeAll = false) {
|
|
200
|
+
const db = this.db;
|
|
201
|
+
let query = 'SELECT * FROM tasks WHERE (title LIKE ? OR body LIKE ?)';
|
|
202
|
+
const params = [`%${keyword}%`, `%${keyword}%`];
|
|
203
|
+
if (!includeAll) {
|
|
204
|
+
query += ' AND status NOT IN (?, ?)';
|
|
205
|
+
params.push('done', 'closed');
|
|
206
|
+
}
|
|
207
|
+
query += ' ORDER BY created_at DESC';
|
|
208
|
+
const stmt = db.prepare(query);
|
|
209
|
+
return stmt.all(...params);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get child tasks (direct children only)
|
|
213
|
+
* @param parentId - Parent task ID
|
|
214
|
+
* @returns Array of child tasks
|
|
215
|
+
*/
|
|
216
|
+
getChildTasks(parentId) {
|
|
217
|
+
const db = this.db;
|
|
218
|
+
const stmt = db.prepare('SELECT * FROM tasks WHERE parent_id = ? ORDER BY created_at ASC');
|
|
219
|
+
return stmt.all(parentId);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get parent task
|
|
223
|
+
* @param taskId - Task ID
|
|
224
|
+
* @returns Parent task, or null if no parent exists
|
|
225
|
+
*/
|
|
226
|
+
getParentTask(taskId) {
|
|
227
|
+
const task = this.getTask(taskId);
|
|
228
|
+
if (!task || !task.parent_id) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
return this.getTask(task.parent_id);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get descendant tasks iteratively (all descendants)
|
|
235
|
+
* @param parentId - Parent task ID
|
|
236
|
+
* @returns Array of all descendant tasks
|
|
237
|
+
*/
|
|
238
|
+
getDescendantTasks(parentId) {
|
|
239
|
+
const descendants = [];
|
|
240
|
+
const visited = new Set();
|
|
241
|
+
const queue = [parentId];
|
|
242
|
+
while (queue.length > 0) {
|
|
243
|
+
const currentId = queue.shift();
|
|
244
|
+
if (visited.has(currentId)) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
visited.add(currentId);
|
|
248
|
+
const children = this.getChildTasks(currentId);
|
|
249
|
+
for (const child of children) {
|
|
250
|
+
if (!visited.has(child.id)) {
|
|
251
|
+
descendants.push(child);
|
|
252
|
+
queue.push(child.id);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return descendants;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Get root task (top-level parent)
|
|
260
|
+
* @param taskId - Task ID
|
|
261
|
+
* @returns Root task, or null if task not found
|
|
262
|
+
*/
|
|
263
|
+
getRootTask(taskId) {
|
|
264
|
+
let currentTask = this.getTask(taskId);
|
|
265
|
+
if (!currentTask) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const visited = new Set();
|
|
269
|
+
visited.add(currentTask.id);
|
|
270
|
+
while (currentTask.parent_id) {
|
|
271
|
+
if (visited.has(currentTask.parent_id)) {
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
const parentTask = this.getTask(currentTask.parent_id);
|
|
275
|
+
if (!parentTask) {
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
visited.add(parentTask.id);
|
|
279
|
+
currentTask = parentTask;
|
|
280
|
+
}
|
|
281
|
+
return currentTask;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
exports.TaskService = TaskService;
|
|
285
|
+
//# sourceMappingURL=TaskService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskService.js","sourceRoot":"","sources":["../../src/services/TaskService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { TaskTag, CreateTaskTagInput, Tag, Task } from '../models';
|
|
2
|
+
import { TaskService } from './TaskService';
|
|
3
|
+
import { TagService } from './TagService';
|
|
4
|
+
import Database from 'better-sqlite3';
|
|
5
|
+
/**
|
|
6
|
+
* Task Tag Service
|
|
7
|
+
* Manages associations between tasks and tags
|
|
8
|
+
*/
|
|
9
|
+
export declare class TaskTagService {
|
|
10
|
+
private db;
|
|
11
|
+
private taskService;
|
|
12
|
+
private tagService;
|
|
13
|
+
constructor(db?: Database.Database, taskService?: TaskService, tagService?: TagService);
|
|
14
|
+
/**
|
|
15
|
+
* Add tag to task
|
|
16
|
+
* @param input - Task tag creation input
|
|
17
|
+
* @returns Created task tag association
|
|
18
|
+
* @throws Error if task or tag does not exist, or if the association already exists
|
|
19
|
+
*/
|
|
20
|
+
addTagToTask(input: CreateTaskTagInput): TaskTag;
|
|
21
|
+
/**
|
|
22
|
+
* Remove tag from task
|
|
23
|
+
* @param taskId - Task ID
|
|
24
|
+
* @param tagId - Tag ID
|
|
25
|
+
* @returns True if removal was successful, false if association not found
|
|
26
|
+
*/
|
|
27
|
+
removeTagFromTask(taskId: number, tagId: number): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Check if task has a tag
|
|
30
|
+
* @param taskId - Task ID
|
|
31
|
+
* @param tagId - Tag ID
|
|
32
|
+
* @returns True if task has the tag, false otherwise
|
|
33
|
+
*/
|
|
34
|
+
hasTag(taskId: number, tagId: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Get tag IDs for a task
|
|
37
|
+
* @param taskId - Task ID
|
|
38
|
+
* @returns Array of tag IDs in order of assignment
|
|
39
|
+
*/
|
|
40
|
+
getTagIdsForTask(taskId: number): number[];
|
|
41
|
+
/**
|
|
42
|
+
* Get tag objects for a task
|
|
43
|
+
* @param taskId - Task ID
|
|
44
|
+
* @returns Array of tag objects in order of assignment
|
|
45
|
+
*/
|
|
46
|
+
getTagsForTask(taskId: number): Tag[];
|
|
47
|
+
/**
|
|
48
|
+
* Get task IDs for a tag
|
|
49
|
+
* @param tagId - Tag ID
|
|
50
|
+
* @returns Array of task IDs
|
|
51
|
+
*/
|
|
52
|
+
getTaskIdsForTag(tagId: number): number[];
|
|
53
|
+
/**
|
|
54
|
+
* Get task objects for a tag
|
|
55
|
+
* @param tagId - Tag ID
|
|
56
|
+
* @returns Array of task objects
|
|
57
|
+
*/
|
|
58
|
+
getTasksForTag(tagId: number): Task[];
|
|
59
|
+
/**
|
|
60
|
+
* Get all task tags at once
|
|
61
|
+
* Avoids the N+1 problem by fetching all task tags in a single query
|
|
62
|
+
* @returns Map<task_id, Tag[]>
|
|
63
|
+
*/
|
|
64
|
+
getAllTaskTags(): Map<number, Tag[]>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=TaskTagService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskTagService.d.ts","sourceRoot":"","sources":["../../src/services/TaskTagService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,UAAU,CAAa;gBAEnB,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,UAAU;IAMtF;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;IAiChD;;;;;OAKG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAazD;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAY9C;;;;OAIG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAa1C;;;;OAIG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,EAAE;IAcrC;;;;OAIG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAazC;;;;OAIG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE;IAcrC;;;;OAIG;IACH,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;CA8BrC"}
|