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.
Files changed (208) hide show
  1. package/README.ja.md +1127 -0
  2. package/README.md +1140 -0
  3. package/bin/agkan +2 -0
  4. package/dist/cli/commands/block/add.d.ts +6 -0
  5. package/dist/cli/commands/block/add.d.ts.map +1 -0
  6. package/dist/cli/commands/block/add.js +135 -0
  7. package/dist/cli/commands/block/add.js.map +1 -0
  8. package/dist/cli/commands/block/list.d.ts +6 -0
  9. package/dist/cli/commands/block/list.d.ts.map +1 -0
  10. package/dist/cli/commands/block/list.js +120 -0
  11. package/dist/cli/commands/block/list.js.map +1 -0
  12. package/dist/cli/commands/block/remove.d.ts +6 -0
  13. package/dist/cli/commands/block/remove.d.ts.map +1 -0
  14. package/dist/cli/commands/block/remove.js +117 -0
  15. package/dist/cli/commands/block/remove.js.map +1 -0
  16. package/dist/cli/commands/meta/delete.d.ts +6 -0
  17. package/dist/cli/commands/meta/delete.d.ts.map +1 -0
  18. package/dist/cli/commands/meta/delete.js +67 -0
  19. package/dist/cli/commands/meta/delete.js.map +1 -0
  20. package/dist/cli/commands/meta/get.d.ts +6 -0
  21. package/dist/cli/commands/meta/get.d.ts.map +1 -0
  22. package/dist/cli/commands/meta/get.js +71 -0
  23. package/dist/cli/commands/meta/get.js.map +1 -0
  24. package/dist/cli/commands/meta/list.d.ts +6 -0
  25. package/dist/cli/commands/meta/list.d.ts.map +1 -0
  26. package/dist/cli/commands/meta/list.js +69 -0
  27. package/dist/cli/commands/meta/list.js.map +1 -0
  28. package/dist/cli/commands/meta/set.d.ts +6 -0
  29. package/dist/cli/commands/meta/set.d.ts.map +1 -0
  30. package/dist/cli/commands/meta/set.js +85 -0
  31. package/dist/cli/commands/meta/set.js.map +1 -0
  32. package/dist/cli/commands/tag/add.d.ts +6 -0
  33. package/dist/cli/commands/tag/add.d.ts.map +1 -0
  34. package/dist/cli/commands/tag/add.js +99 -0
  35. package/dist/cli/commands/tag/add.js.map +1 -0
  36. package/dist/cli/commands/tag/attach.d.ts +6 -0
  37. package/dist/cli/commands/tag/attach.d.ts.map +1 -0
  38. package/dist/cli/commands/tag/attach.js +119 -0
  39. package/dist/cli/commands/tag/attach.js.map +1 -0
  40. package/dist/cli/commands/tag/delete.d.ts +6 -0
  41. package/dist/cli/commands/tag/delete.d.ts.map +1 -0
  42. package/dist/cli/commands/tag/delete.js +93 -0
  43. package/dist/cli/commands/tag/delete.js.map +1 -0
  44. package/dist/cli/commands/tag/detach.d.ts +6 -0
  45. package/dist/cli/commands/tag/detach.d.ts.map +1 -0
  46. package/dist/cli/commands/tag/detach.js +118 -0
  47. package/dist/cli/commands/tag/detach.js.map +1 -0
  48. package/dist/cli/commands/tag/list.d.ts +6 -0
  49. package/dist/cli/commands/tag/list.d.ts.map +1 -0
  50. package/dist/cli/commands/tag/list.js +80 -0
  51. package/dist/cli/commands/tag/list.js.map +1 -0
  52. package/dist/cli/commands/tag/show.d.ts +6 -0
  53. package/dist/cli/commands/tag/show.d.ts.map +1 -0
  54. package/dist/cli/commands/tag/show.js +88 -0
  55. package/dist/cli/commands/tag/show.js.map +1 -0
  56. package/dist/cli/commands/task/add-helpers.d.ts +17 -0
  57. package/dist/cli/commands/task/add-helpers.d.ts.map +1 -0
  58. package/dist/cli/commands/task/add-helpers.js +122 -0
  59. package/dist/cli/commands/task/add-helpers.js.map +1 -0
  60. package/dist/cli/commands/task/add.d.ts +6 -0
  61. package/dist/cli/commands/task/add.d.ts.map +1 -0
  62. package/dist/cli/commands/task/add.js +140 -0
  63. package/dist/cli/commands/task/add.js.map +1 -0
  64. package/dist/cli/commands/task/count.d.ts +6 -0
  65. package/dist/cli/commands/task/count.d.ts.map +1 -0
  66. package/dist/cli/commands/task/count.js +97 -0
  67. package/dist/cli/commands/task/count.js.map +1 -0
  68. package/dist/cli/commands/task/delete.d.ts +6 -0
  69. package/dist/cli/commands/task/delete.d.ts.map +1 -0
  70. package/dist/cli/commands/task/delete.js +59 -0
  71. package/dist/cli/commands/task/delete.js.map +1 -0
  72. package/dist/cli/commands/task/find.d.ts +7 -0
  73. package/dist/cli/commands/task/find.d.ts.map +1 -0
  74. package/dist/cli/commands/task/find.js +118 -0
  75. package/dist/cli/commands/task/find.js.map +1 -0
  76. package/dist/cli/commands/task/get.d.ts +6 -0
  77. package/dist/cli/commands/task/get.d.ts.map +1 -0
  78. package/dist/cli/commands/task/get.js +196 -0
  79. package/dist/cli/commands/task/get.js.map +1 -0
  80. package/dist/cli/commands/task/list.d.ts +6 -0
  81. package/dist/cli/commands/task/list.d.ts.map +1 -0
  82. package/dist/cli/commands/task/list.js +301 -0
  83. package/dist/cli/commands/task/list.js.map +1 -0
  84. package/dist/cli/commands/task/update-parent.d.ts +6 -0
  85. package/dist/cli/commands/task/update-parent.d.ts.map +1 -0
  86. package/dist/cli/commands/task/update-parent.js +123 -0
  87. package/dist/cli/commands/task/update-parent.js.map +1 -0
  88. package/dist/cli/commands/task/update.d.ts +6 -0
  89. package/dist/cli/commands/task/update.d.ts.map +1 -0
  90. package/dist/cli/commands/task/update.js +96 -0
  91. package/dist/cli/commands/task/update.js.map +1 -0
  92. package/dist/cli/index.d.ts +3 -0
  93. package/dist/cli/index.d.ts.map +1 -0
  94. package/dist/cli/index.js +68 -0
  95. package/dist/cli/index.js.map +1 -0
  96. package/dist/cli/utils/array-utils.d.ts +15 -0
  97. package/dist/cli/utils/array-utils.d.ts.map +1 -0
  98. package/dist/cli/utils/array-utils.js +20 -0
  99. package/dist/cli/utils/array-utils.js.map +1 -0
  100. package/dist/cli/utils/error-handler.d.ts +35 -0
  101. package/dist/cli/utils/error-handler.d.ts.map +1 -0
  102. package/dist/cli/utils/error-handler.js +84 -0
  103. package/dist/cli/utils/error-handler.js.map +1 -0
  104. package/dist/cli/utils/output-formatter.d.ts +34 -0
  105. package/dist/cli/utils/output-formatter.d.ts.map +1 -0
  106. package/dist/cli/utils/output-formatter.js +44 -0
  107. package/dist/cli/utils/output-formatter.js.map +1 -0
  108. package/dist/cli/utils/response-formatter.d.ts +19 -0
  109. package/dist/cli/utils/response-formatter.d.ts.map +1 -0
  110. package/dist/cli/utils/response-formatter.js +43 -0
  111. package/dist/cli/utils/response-formatter.js.map +1 -0
  112. package/dist/cli/utils/validators.d.ts +23 -0
  113. package/dist/cli/utils/validators.d.ts.map +1 -0
  114. package/dist/cli/utils/validators.js +47 -0
  115. package/dist/cli/utils/validators.js.map +1 -0
  116. package/dist/db/config.d.ts +27 -0
  117. package/dist/db/config.d.ts.map +1 -0
  118. package/dist/db/config.js +116 -0
  119. package/dist/db/config.js.map +1 -0
  120. package/dist/db/connection.d.ts +24 -0
  121. package/dist/db/connection.d.ts.map +1 -0
  122. package/dist/db/connection.js +62 -0
  123. package/dist/db/connection.js.map +1 -0
  124. package/dist/db/reset.d.ts +8 -0
  125. package/dist/db/reset.d.ts.map +1 -0
  126. package/dist/db/reset.js +33 -0
  127. package/dist/db/reset.js.map +1 -0
  128. package/dist/db/schema.d.ts +6 -0
  129. package/dist/db/schema.d.ts.map +1 -0
  130. package/dist/db/schema.js +134 -0
  131. package/dist/db/schema.js.map +1 -0
  132. package/dist/models/Attachment.d.ts +25 -0
  133. package/dist/models/Attachment.d.ts.map +1 -0
  134. package/dist/models/Attachment.js +7 -0
  135. package/dist/models/Attachment.js.map +1 -0
  136. package/dist/models/Tag.d.ts +24 -0
  137. package/dist/models/Tag.d.ts.map +1 -0
  138. package/dist/models/Tag.js +3 -0
  139. package/dist/models/Tag.js.map +1 -0
  140. package/dist/models/Task.d.ts +55 -0
  141. package/dist/models/Task.d.ts.map +1 -0
  142. package/dist/models/Task.js +7 -0
  143. package/dist/models/Task.js.map +1 -0
  144. package/dist/models/TaskBlock.d.ts +11 -0
  145. package/dist/models/TaskBlock.d.ts.map +1 -0
  146. package/dist/models/TaskBlock.js +3 -0
  147. package/dist/models/TaskBlock.js.map +1 -0
  148. package/dist/models/TaskMetadata.d.ts +30 -0
  149. package/dist/models/TaskMetadata.d.ts.map +1 -0
  150. package/dist/models/TaskMetadata.js +3 -0
  151. package/dist/models/TaskMetadata.js.map +1 -0
  152. package/dist/models/TaskTag.d.ts +11 -0
  153. package/dist/models/TaskTag.d.ts.map +1 -0
  154. package/dist/models/TaskTag.js +3 -0
  155. package/dist/models/TaskTag.js.map +1 -0
  156. package/dist/models/index.d.ts +10 -0
  157. package/dist/models/index.d.ts.map +1 -0
  158. package/dist/models/index.js +7 -0
  159. package/dist/models/index.js.map +1 -0
  160. package/dist/services/AttachmentService.d.ts +62 -0
  161. package/dist/services/AttachmentService.d.ts.map +1 -0
  162. package/dist/services/AttachmentService.js +95 -0
  163. package/dist/services/AttachmentService.js.map +1 -0
  164. package/dist/services/FileService.d.ts +31 -0
  165. package/dist/services/FileService.d.ts.map +1 -0
  166. package/dist/services/FileService.js +77 -0
  167. package/dist/services/FileService.js.map +1 -0
  168. package/dist/services/MetadataService.d.ts +49 -0
  169. package/dist/services/MetadataService.d.ts.map +1 -0
  170. package/dist/services/MetadataService.js +126 -0
  171. package/dist/services/MetadataService.js.map +1 -0
  172. package/dist/services/TagService.d.ts +49 -0
  173. package/dist/services/TagService.d.ts.map +1 -0
  174. package/dist/services/TagService.js +127 -0
  175. package/dist/services/TagService.js.map +1 -0
  176. package/dist/services/TaskBlockService.d.ts +49 -0
  177. package/dist/services/TaskBlockService.d.ts.map +1 -0
  178. package/dist/services/TaskBlockService.js +118 -0
  179. package/dist/services/TaskBlockService.js.map +1 -0
  180. package/dist/services/TaskService.d.ts +89 -0
  181. package/dist/services/TaskService.d.ts.map +1 -0
  182. package/dist/services/TaskService.js +285 -0
  183. package/dist/services/TaskService.js.map +1 -0
  184. package/dist/services/TaskTagService.d.ts +66 -0
  185. package/dist/services/TaskTagService.d.ts.map +1 -0
  186. package/dist/services/TaskTagService.js +171 -0
  187. package/dist/services/TaskTagService.js.map +1 -0
  188. package/dist/services/index.d.ts +11 -0
  189. package/dist/services/index.d.ts.map +1 -0
  190. package/dist/services/index.js +20 -0
  191. package/dist/services/index.js.map +1 -0
  192. package/dist/utils/cycle-detector.d.ts +14 -0
  193. package/dist/utils/cycle-detector.d.ts.map +1 -0
  194. package/dist/utils/cycle-detector.js +32 -0
  195. package/dist/utils/cycle-detector.js.map +1 -0
  196. package/dist/utils/format.d.ts +13 -0
  197. package/dist/utils/format.d.ts.map +1 -0
  198. package/dist/utils/format.js +29 -0
  199. package/dist/utils/format.js.map +1 -0
  200. package/dist/utils/input-validators.d.ts +32 -0
  201. package/dist/utils/input-validators.d.ts.map +1 -0
  202. package/dist/utils/input-validators.js +108 -0
  203. package/dist/utils/input-validators.js.map +1 -0
  204. package/dist/utils/security.d.ts +11 -0
  205. package/dist/utils/security.d.ts.map +1 -0
  206. package/dist/utils/security.js +29 -0
  207. package/dist/utils/security.js.map +1 -0
  208. 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":";;;AACA,iDAA+C;AAC/C,gEAAuF;AACvF,4DAA2D;AAG3D;;;GAGG;AACH,MAAa,WAAW;IAGtB,YAAY,EAAsB;QAChC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,IAAA,wBAAW,GAAE,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,KAAsB;QAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC;QAEzC,wBAAwB;QACxB,MAAM,gBAAgB,GAAG,IAAA,oCAAiB,EAAC,KAAK,CAAC,CAAC;QAClD,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,kDAAkD;QAClD,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC9D,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACjD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,KAAK,CAAC,SAAS,iBAAiB,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;KAGvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACrB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,IAAI,IAAI,IAAI,EAClB,KAAK,CAAC,MAAM,IAAI,IAAI,EACpB,MAAM,EACN,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EACtD,GAAG,EACH,GAAG,CACJ,CAAC;QAEF,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,eAAyB,CAAE,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,EAAU;QAChB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAEnB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAqB,CAAC;QAE9C,OAAO,IAAI,IAAI,IAAI,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,OAAqE;QAC7E,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAEnB,IAAI,KAAa,CAAC;QAClB,MAAM,MAAM,GAAwB,EAAE,CAAC;QAEvC,wCAAwC;QACxC,IAAI,OAAO,EAAE,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjD,KAAK,GAAG,mGAAmG,CAAC;YAE5G,4BAA4B;YAC5B,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9D,KAAK,IAAI,6BAA6B,YAAY,GAAG,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,+BAA+B,CAAC;QAC1C,CAAC;QAED,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,KAAK,IAAI,iBAAiB,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,KAAK,IAAI,iBAAiB,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAED,KAAK,IAAI,2BAA2B,CAAC;QAErC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAW,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACK,gBAAgB,CAAC,KAAsB,EAAE,EAAU;QACzD,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,MAAM,GAA+B,EAAE,CAAC;QAE9C,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC/B,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEhB,MAAM,GAAG,GAAG,oBAAoB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;QAClE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,EAAU,EAAE,KAAsB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAA,0CAAuB,EAAC,KAAK,CAAC,CAAC;QACxD,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,IAAA,iCAAgB,EAAC,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,SAAS,IAAI,IAAI,CAAC,EAAE,CAAC;gBACnG,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,SAAS,mCAAmC,CAAC,CAAC;YACjG,CAAC;YAED,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;gBAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACjD,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,KAAK,CAAC,SAAS,iBAAiB,CAAC,CAAC;gBAC3E,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QAEpC,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,EAAU;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAEnB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;QAC1D,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEb,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,oBAAoB;QAClB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAEnB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;;KAIvB,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAkD,CAAC;QAE3E,iCAAiC;QACjC,MAAM,QAAQ,GAA+B;YAC3C,OAAO,EAAE,CAAC;YACV,KAAK,EAAE,CAAC;YACR,WAAW,EAAE,CAAC;YACd,MAAM,EAAE,CAAC;YACT,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,CAAC;SACV,CAAC;QAEF,gCAAgC;QAChC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACtB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAC,OAAe,EAAE,aAAsB,KAAK;QACtD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAEnB,IAAI,KAAK,GAAG,yDAAyD,CAAC;QACtE,MAAM,MAAM,GAAa,CAAC,IAAI,OAAO,GAAG,EAAE,IAAI,OAAO,GAAG,CAAC,CAAC;QAE1D,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,KAAK,IAAI,2BAA2B,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChC,CAAC;QAED,KAAK,IAAI,2BAA2B,CAAC;QAErC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAW,CAAC;IACvC,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,QAAgB;QAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,iEAAiE,CAAC,CAAC;QAC3F,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAW,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,MAAc;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,QAAgB;QACjC,MAAM,WAAW,GAAW,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEzB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;YACjC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3B,SAAS;YACX,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YAC/C,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;gBAC7B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC3B,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACxB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,MAAc;QACxB,IAAI,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAE5B,OAAO,WAAW,CAAC,SAAS,EAAE,CAAC;YAC7B,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvC,MAAM;YACR,CAAC;YACD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YACvD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM;YACR,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC3B,WAAW,GAAG,UAAU,CAAC;QAC3B,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;CACF;AAjVD,kCAiVC"}
@@ -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"}