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,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AttachmentService = void 0;
|
|
4
|
+
const connection_1 = require("../db/connection");
|
|
5
|
+
/**
|
|
6
|
+
* Attachment Service
|
|
7
|
+
* Provides management of attachment records
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* **Future Feature**: This service is fully implemented but CLI commands are not yet available.
|
|
11
|
+
* See docs/planned-features.md for implementation timeline and planned CLI interface.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const service = new AttachmentService();
|
|
16
|
+
* const attachment = service.createAttachment({
|
|
17
|
+
* task_id: 1,
|
|
18
|
+
* file_name: 'design.pdf',
|
|
19
|
+
* file_path: '/path/to/design.pdf'
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
class AttachmentService {
|
|
24
|
+
constructor(db) {
|
|
25
|
+
this.db = db || (0, connection_1.getDatabase)();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a new attachment record
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* **Future Feature**: CLI command not yet implemented.
|
|
32
|
+
* Planned command: `agkan task attach add <task-id> <file-path>`
|
|
33
|
+
*
|
|
34
|
+
* @param input - Attachment creation input
|
|
35
|
+
* @returns Created attachment record
|
|
36
|
+
* @throws Will throw if task_id does not exist (foreign key constraint)
|
|
37
|
+
*/
|
|
38
|
+
createAttachment(input) {
|
|
39
|
+
const db = this.db;
|
|
40
|
+
const now = new Date().toISOString();
|
|
41
|
+
const stmt = db.prepare(`
|
|
42
|
+
INSERT INTO attachments (task_id, file_name, file_path, created_at)
|
|
43
|
+
VALUES (?, ?, ?, ?)
|
|
44
|
+
`);
|
|
45
|
+
const result = stmt.run(input.task_id, input.file_name, input.file_path, now);
|
|
46
|
+
return {
|
|
47
|
+
id: result.lastInsertRowid,
|
|
48
|
+
task_id: input.task_id,
|
|
49
|
+
file_name: input.file_name,
|
|
50
|
+
file_path: input.file_path,
|
|
51
|
+
created_at: now,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get attachments for a specific task
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
* **Future Feature**: CLI command not yet implemented.
|
|
59
|
+
* Planned command: `agkan task attach list <task-id>`
|
|
60
|
+
*
|
|
61
|
+
* @param taskId - Task ID
|
|
62
|
+
* @returns Array of attachments ordered by creation date (newest first)
|
|
63
|
+
*/
|
|
64
|
+
getAttachmentsByTaskId(taskId) {
|
|
65
|
+
const db = this.db;
|
|
66
|
+
const stmt = db.prepare('SELECT * FROM attachments WHERE task_id = ? ORDER BY created_at DESC');
|
|
67
|
+
return stmt.all(taskId);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Delete an attachment record
|
|
71
|
+
*
|
|
72
|
+
* @remarks
|
|
73
|
+
* **Future Feature**: CLI command not yet implemented.
|
|
74
|
+
* Planned command: `agkan task attach delete <attachment-id>`
|
|
75
|
+
*
|
|
76
|
+
* @param id - Attachment ID
|
|
77
|
+
* @returns True if deletion succeeded, false if not found
|
|
78
|
+
*
|
|
79
|
+
* @todo Implement file deletion from filesystem
|
|
80
|
+
* @todo Add --keep-file option to CLI command
|
|
81
|
+
*/
|
|
82
|
+
deleteAttachment(id) {
|
|
83
|
+
const db = this.db;
|
|
84
|
+
const stmt = db.prepare('SELECT * FROM attachments WHERE id = ?');
|
|
85
|
+
const attachment = stmt.get(id);
|
|
86
|
+
if (!attachment) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const deleteStmt = db.prepare('DELETE FROM attachments WHERE id = ?');
|
|
90
|
+
deleteStmt.run(id);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.AttachmentService = AttachmentService;
|
|
95
|
+
//# sourceMappingURL=AttachmentService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttachmentService.js","sourceRoot":"","sources":["../../src/services/AttachmentService.ts"],"names":[],"mappings":";;;AACA,iDAA+C;AAG/C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,iBAAiB;IAG5B,YAAY,EAAsB;QAChC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,IAAA,wBAAW,GAAE,CAAC;IAChC,CAAC;IAED;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,KAA4B;QAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAErC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;KAGvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAE9E,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,eAAyB;YACpC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,UAAU,EAAE,GAAG;SAChB,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,sBAAsB,CAAC,MAAc;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAEnB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,sEAAsE,CAAC,CAAC;QAChG,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAiB,CAAC;IAC1C,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,EAAU;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAEnB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;QACtE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEnB,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAnFD,8CAmFC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Service
|
|
3
|
+
* Provides file system operations
|
|
4
|
+
*/
|
|
5
|
+
export declare class FileService {
|
|
6
|
+
/**
|
|
7
|
+
* Save file to attachments directory
|
|
8
|
+
* @param sourceFile - Source file path
|
|
9
|
+
* @param taskId - Related task ID
|
|
10
|
+
* @returns Saved file name and path
|
|
11
|
+
* @throws If file not found
|
|
12
|
+
*/
|
|
13
|
+
saveFile(sourceFile: string, taskId: number): {
|
|
14
|
+
file_name: string;
|
|
15
|
+
file_path: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Read markdown file contents
|
|
19
|
+
* @param filePath - Path of file to read
|
|
20
|
+
* @returns File contents
|
|
21
|
+
* @throws If file not found or path is unsafe
|
|
22
|
+
*/
|
|
23
|
+
readMarkdownFile(filePath: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Delete file
|
|
26
|
+
* @param filePath - Path of file to delete
|
|
27
|
+
* @returns Returns true if deletion succeeds, false if file does not exist
|
|
28
|
+
*/
|
|
29
|
+
deleteFile(filePath: string): boolean;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=FileService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileService.d.ts","sourceRoot":"","sources":["../../src/services/FileService.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,qBAAa,WAAW;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IA2BtF;;;;;OAKG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAY1C;;;;OAIG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAYtC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FileService = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const security_1 = require("../utils/security");
|
|
10
|
+
/**
|
|
11
|
+
* File Service
|
|
12
|
+
* Provides file system operations
|
|
13
|
+
*/
|
|
14
|
+
class FileService {
|
|
15
|
+
/**
|
|
16
|
+
* Save file to attachments directory
|
|
17
|
+
* @param sourceFile - Source file path
|
|
18
|
+
* @param taskId - Related task ID
|
|
19
|
+
* @returns Saved file name and path
|
|
20
|
+
* @throws If file not found
|
|
21
|
+
*/
|
|
22
|
+
saveFile(sourceFile, taskId) {
|
|
23
|
+
// Check if file exists
|
|
24
|
+
if (!fs_1.default.existsSync(sourceFile)) {
|
|
25
|
+
throw new Error(`File not found: ${sourceFile}`);
|
|
26
|
+
}
|
|
27
|
+
// Get file name
|
|
28
|
+
const fileName = path_1.default.basename(sourceFile);
|
|
29
|
+
// Create destination directory (attachments/<task_id>/)
|
|
30
|
+
const attachmentDir = path_1.default.join(process.cwd(), 'attachments', taskId.toString());
|
|
31
|
+
fs_1.default.mkdirSync(attachmentDir, { recursive: true });
|
|
32
|
+
// Generate filename with timestamp
|
|
33
|
+
const timestamp = Date.now();
|
|
34
|
+
const destFileName = `${timestamp}_${fileName}`;
|
|
35
|
+
const destPath = path_1.default.join(attachmentDir, destFileName);
|
|
36
|
+
// Copy file
|
|
37
|
+
fs_1.default.copyFileSync(sourceFile, destPath);
|
|
38
|
+
return {
|
|
39
|
+
file_name: fileName,
|
|
40
|
+
file_path: destPath,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read markdown file contents
|
|
45
|
+
* @param filePath - Path of file to read
|
|
46
|
+
* @returns File contents
|
|
47
|
+
* @throws If file not found or path is unsafe
|
|
48
|
+
*/
|
|
49
|
+
readMarkdownFile(filePath) {
|
|
50
|
+
if (!(0, security_1.isPathSafe)(filePath)) {
|
|
51
|
+
throw new Error(`Unsafe file path: ${filePath}`);
|
|
52
|
+
}
|
|
53
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
54
|
+
throw new Error(`File not found: ${filePath}`);
|
|
55
|
+
}
|
|
56
|
+
return fs_1.default.readFileSync(filePath, 'utf-8');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Delete file
|
|
60
|
+
* @param filePath - Path of file to delete
|
|
61
|
+
* @returns Returns true if deletion succeeds, false if file does not exist
|
|
62
|
+
*/
|
|
63
|
+
deleteFile(filePath) {
|
|
64
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
fs_1.default.unlinkSync(filePath);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new Error(`Failed to delete file: ${filePath}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.FileService = FileService;
|
|
77
|
+
//# sourceMappingURL=FileService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileService.js","sourceRoot":"","sources":["../../src/services/FileService.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,gDAA+C;AAE/C;;;GAGG;AACH,MAAa,WAAW;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAkB,EAAE,MAAc;QACzC,uBAAuB;QACvB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,gBAAgB;QAChB,MAAM,QAAQ,GAAG,cAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAE3C,wDAAwD;QACxD,MAAM,aAAa,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjF,YAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjD,mCAAmC;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,YAAY,GAAG,GAAG,SAAS,IAAI,QAAQ,EAAE,CAAC;QAChD,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;QAExD,YAAY;QACZ,YAAE,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAEtC,OAAO;YACL,SAAS,EAAE,QAAQ;YACnB,SAAS,EAAE,QAAQ;SACpB,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,gBAAgB,CAAC,QAAgB;QAC/B,IAAI,CAAC,IAAA,qBAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,OAAO,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,QAAgB;QACzB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;CACF;AAtED,kCAsEC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { TaskMetadata, CreateTaskMetadataInput } from '../models';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
/**
|
|
4
|
+
* Metadata Service
|
|
5
|
+
* Manages creation, retrieval, update, and deletion of task metadata
|
|
6
|
+
*/
|
|
7
|
+
export declare class MetadataService {
|
|
8
|
+
private db;
|
|
9
|
+
constructor(db?: Database.Database);
|
|
10
|
+
/**
|
|
11
|
+
* Set metadata for a task (create or update)
|
|
12
|
+
* @param input - Metadata creation input
|
|
13
|
+
* @returns Metadata object
|
|
14
|
+
*/
|
|
15
|
+
setMetadata(input: CreateTaskMetadataInput): TaskMetadata;
|
|
16
|
+
/**
|
|
17
|
+
* Get metadata by task ID and key
|
|
18
|
+
* @param taskId - Task ID
|
|
19
|
+
* @param key - Metadata key
|
|
20
|
+
* @returns Metadata object or null if not found
|
|
21
|
+
*/
|
|
22
|
+
getMetadataByKey(taskId: number, key: string): TaskMetadata | null;
|
|
23
|
+
/**
|
|
24
|
+
* List metadata for a task
|
|
25
|
+
* @param taskId - Task ID
|
|
26
|
+
* @returns Array of metadata objects
|
|
27
|
+
*/
|
|
28
|
+
listMetadata(taskId: number): TaskMetadata[];
|
|
29
|
+
/**
|
|
30
|
+
* Delete metadata by task ID and key
|
|
31
|
+
* @param taskId - Task ID
|
|
32
|
+
* @param key - Metadata key
|
|
33
|
+
* @returns True if deletion succeeded, false if metadata not found
|
|
34
|
+
*/
|
|
35
|
+
deleteMetadata(taskId: number, key: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Get all metadata for multiple tasks at once
|
|
38
|
+
* Avoids the N+1 problem by fetching all metadata in a single query
|
|
39
|
+
* @returns Map<task_id, TaskMetadata[]>
|
|
40
|
+
*/
|
|
41
|
+
getAllTasksMetadata(): Map<number, TaskMetadata[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Delete all metadata for a task
|
|
44
|
+
* @param taskId - Task ID
|
|
45
|
+
* @returns Number of deleted metadata entries
|
|
46
|
+
*/
|
|
47
|
+
deleteAllMetadata(taskId: number): number;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=MetadataService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetadataService.d.ts","sourceRoot":"","sources":["../../src/services/MetadataService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAElE,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAoB;gBAElB,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ;IAIlC;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,uBAAuB,GAAG,YAAY;IAgCzD;;;;;OAKG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAalE;;;;OAIG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE;IAc5C;;;;;OAKG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAapD;;;;OAIG;IACH,mBAAmB,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC;IAsBlD;;;;OAIG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAY1C"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MetadataService = void 0;
|
|
4
|
+
const connection_1 = require("../db/connection");
|
|
5
|
+
/**
|
|
6
|
+
* Metadata Service
|
|
7
|
+
* Manages creation, retrieval, update, and deletion of task metadata
|
|
8
|
+
*/
|
|
9
|
+
class MetadataService {
|
|
10
|
+
constructor(db) {
|
|
11
|
+
this.db = db || (0, connection_1.getDatabase)();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Set metadata for a task (create or update)
|
|
15
|
+
* @param input - Metadata creation input
|
|
16
|
+
* @returns Metadata object
|
|
17
|
+
*/
|
|
18
|
+
setMetadata(input) {
|
|
19
|
+
const db = this.db;
|
|
20
|
+
// Check if metadata already exists
|
|
21
|
+
const existing = this.getMetadataByKey(input.task_id, input.key);
|
|
22
|
+
const now = new Date().toISOString();
|
|
23
|
+
if (existing) {
|
|
24
|
+
// Update existing metadata
|
|
25
|
+
const stmt = db.prepare(`
|
|
26
|
+
UPDATE task_metadata
|
|
27
|
+
SET value = ?, updated_at = ?
|
|
28
|
+
WHERE task_id = ? AND key = ?
|
|
29
|
+
`);
|
|
30
|
+
stmt.run(input.value, now, input.task_id, input.key);
|
|
31
|
+
return this.getMetadataByKey(input.task_id, input.key);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Create new metadata
|
|
35
|
+
const stmt = db.prepare(`
|
|
36
|
+
INSERT INTO task_metadata (task_id, key, value, created_at, updated_at)
|
|
37
|
+
VALUES (?, ?, ?, ?, ?)
|
|
38
|
+
`);
|
|
39
|
+
stmt.run(input.task_id, input.key, input.value, now, now);
|
|
40
|
+
return this.getMetadataByKey(input.task_id, input.key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get metadata by task ID and key
|
|
45
|
+
* @param taskId - Task ID
|
|
46
|
+
* @param key - Metadata key
|
|
47
|
+
* @returns Metadata object or null if not found
|
|
48
|
+
*/
|
|
49
|
+
getMetadataByKey(taskId, key) {
|
|
50
|
+
const db = this.db;
|
|
51
|
+
const stmt = db.prepare(`
|
|
52
|
+
SELECT * FROM task_metadata
|
|
53
|
+
WHERE task_id = ? AND key = ?
|
|
54
|
+
`);
|
|
55
|
+
const result = stmt.get(taskId, key);
|
|
56
|
+
return result ? result : null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* List metadata for a task
|
|
60
|
+
* @param taskId - Task ID
|
|
61
|
+
* @returns Array of metadata objects
|
|
62
|
+
*/
|
|
63
|
+
listMetadata(taskId) {
|
|
64
|
+
const db = this.db;
|
|
65
|
+
const stmt = db.prepare(`
|
|
66
|
+
SELECT * FROM task_metadata
|
|
67
|
+
WHERE task_id = ?
|
|
68
|
+
ORDER BY created_at DESC
|
|
69
|
+
`);
|
|
70
|
+
const results = stmt.all(taskId);
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Delete metadata by task ID and key
|
|
75
|
+
* @param taskId - Task ID
|
|
76
|
+
* @param key - Metadata key
|
|
77
|
+
* @returns True if deletion succeeded, false if metadata not found
|
|
78
|
+
*/
|
|
79
|
+
deleteMetadata(taskId, key) {
|
|
80
|
+
const db = this.db;
|
|
81
|
+
const stmt = db.prepare(`
|
|
82
|
+
DELETE FROM task_metadata
|
|
83
|
+
WHERE task_id = ? AND key = ?
|
|
84
|
+
`);
|
|
85
|
+
const result = stmt.run(taskId, key);
|
|
86
|
+
return result.changes > 0;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get all metadata for multiple tasks at once
|
|
90
|
+
* Avoids the N+1 problem by fetching all metadata in a single query
|
|
91
|
+
* @returns Map<task_id, TaskMetadata[]>
|
|
92
|
+
*/
|
|
93
|
+
getAllTasksMetadata() {
|
|
94
|
+
const db = this.db;
|
|
95
|
+
const stmt = db.prepare(`
|
|
96
|
+
SELECT * FROM task_metadata
|
|
97
|
+
ORDER BY task_id, created_at DESC
|
|
98
|
+
`);
|
|
99
|
+
const results = stmt.all();
|
|
100
|
+
const metadataMap = new Map();
|
|
101
|
+
for (const row of results) {
|
|
102
|
+
const taskId = row.task_id;
|
|
103
|
+
if (!metadataMap.has(taskId)) {
|
|
104
|
+
metadataMap.set(taskId, []);
|
|
105
|
+
}
|
|
106
|
+
metadataMap.get(taskId).push(row);
|
|
107
|
+
}
|
|
108
|
+
return metadataMap;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Delete all metadata for a task
|
|
112
|
+
* @param taskId - Task ID
|
|
113
|
+
* @returns Number of deleted metadata entries
|
|
114
|
+
*/
|
|
115
|
+
deleteAllMetadata(taskId) {
|
|
116
|
+
const db = this.db;
|
|
117
|
+
const stmt = db.prepare(`
|
|
118
|
+
DELETE FROM task_metadata
|
|
119
|
+
WHERE task_id = ?
|
|
120
|
+
`);
|
|
121
|
+
const result = stmt.run(taskId);
|
|
122
|
+
return result.changes;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.MetadataService = MetadataService;
|
|
126
|
+
//# sourceMappingURL=MetadataService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetadataService.js","sourceRoot":"","sources":["../../src/services/MetadataService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Tag, CreateTagInput, UpdateTagInput } from '../models';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
/**
|
|
4
|
+
* Tag Service
|
|
5
|
+
* Manages creation, retrieval, update, and deletion of tags
|
|
6
|
+
*/
|
|
7
|
+
export declare class TagService {
|
|
8
|
+
private db;
|
|
9
|
+
constructor(db?: Database.Database);
|
|
10
|
+
/**
|
|
11
|
+
* Create a new tag
|
|
12
|
+
* @param input - Tag creation input
|
|
13
|
+
* @returns Newly created tag
|
|
14
|
+
* @throws Error if tag name already exists
|
|
15
|
+
*/
|
|
16
|
+
createTag(input: CreateTagInput): Tag;
|
|
17
|
+
/**
|
|
18
|
+
* Get tag by ID
|
|
19
|
+
* @param id - Tag ID
|
|
20
|
+
* @returns Tag object or null if not found
|
|
21
|
+
*/
|
|
22
|
+
getTag(id: number): Tag | null;
|
|
23
|
+
/**
|
|
24
|
+
* Get tag by name
|
|
25
|
+
* @param name - Tag name
|
|
26
|
+
* @returns Tag object or null if not found
|
|
27
|
+
*/
|
|
28
|
+
getTagByName(name: string): Tag | null;
|
|
29
|
+
/**
|
|
30
|
+
* List all tags
|
|
31
|
+
* @returns Array of tags sorted by creation date in descending order
|
|
32
|
+
*/
|
|
33
|
+
listTags(): Tag[];
|
|
34
|
+
/**
|
|
35
|
+
* Update tag
|
|
36
|
+
* @param id - Tag ID
|
|
37
|
+
* @param input - Tag update input
|
|
38
|
+
* @returns Updated tag object or null if not found
|
|
39
|
+
* @throws Error if tag name already exists
|
|
40
|
+
*/
|
|
41
|
+
updateTag(id: number, input: UpdateTagInput): Tag | null;
|
|
42
|
+
/**
|
|
43
|
+
* Delete tag
|
|
44
|
+
* @param id - Tag ID
|
|
45
|
+
* @returns True if deletion succeeded, false if tag not found
|
|
46
|
+
*/
|
|
47
|
+
deleteTag(id: number): boolean;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=TagService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TagService.d.ts","sourceRoot":"","sources":["../../src/services/TagService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhE,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAoB;gBAElB,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ;IAGlC;;;;;OAKG;IACH,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,GAAG;IA4BrC;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAS9B;;;;OAIG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAStC;;;OAGG;IACH,QAAQ,IAAI,GAAG,EAAE;IASjB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,GAAG,GAAG,IAAI;IAyCxD;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAQ/B"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TagService = void 0;
|
|
4
|
+
const connection_1 = require("../db/connection");
|
|
5
|
+
const input_validators_1 = require("../utils/input-validators");
|
|
6
|
+
/**
|
|
7
|
+
* Tag Service
|
|
8
|
+
* Manages creation, retrieval, update, and deletion of tags
|
|
9
|
+
*/
|
|
10
|
+
class TagService {
|
|
11
|
+
constructor(db) {
|
|
12
|
+
this.db = db || (0, connection_1.getDatabase)();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a new tag
|
|
16
|
+
* @param input - Tag creation input
|
|
17
|
+
* @returns Newly created tag
|
|
18
|
+
* @throws Error if tag name already exists
|
|
19
|
+
*/
|
|
20
|
+
createTag(input) {
|
|
21
|
+
const db = this.db;
|
|
22
|
+
// Validate input fields
|
|
23
|
+
const validationErrors = (0, input_validators_1.validateTagInput)(input);
|
|
24
|
+
if (validationErrors.length > 0) {
|
|
25
|
+
throw new Error(validationErrors[0].message);
|
|
26
|
+
}
|
|
27
|
+
// Check for duplicate tag names
|
|
28
|
+
const existingTag = this.getTagByName(input.name);
|
|
29
|
+
if (existingTag) {
|
|
30
|
+
throw new Error(`Tag with name "${input.name}" already exists`);
|
|
31
|
+
}
|
|
32
|
+
const now = new Date().toISOString();
|
|
33
|
+
const stmt = db.prepare(`
|
|
34
|
+
INSERT INTO tags (name, created_at)
|
|
35
|
+
VALUES (?, ?)
|
|
36
|
+
`);
|
|
37
|
+
const result = stmt.run(input.name, now);
|
|
38
|
+
const getStmt = db.prepare('SELECT * FROM tags WHERE id = ?');
|
|
39
|
+
return getStmt.get(result.lastInsertRowid);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get tag by ID
|
|
43
|
+
* @param id - Tag ID
|
|
44
|
+
* @returns Tag object or null if not found
|
|
45
|
+
*/
|
|
46
|
+
getTag(id) {
|
|
47
|
+
const db = this.db;
|
|
48
|
+
const stmt = db.prepare('SELECT * FROM tags WHERE id = ?');
|
|
49
|
+
const result = stmt.get(id);
|
|
50
|
+
return result ? result : null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get tag by name
|
|
54
|
+
* @param name - Tag name
|
|
55
|
+
* @returns Tag object or null if not found
|
|
56
|
+
*/
|
|
57
|
+
getTagByName(name) {
|
|
58
|
+
const db = this.db;
|
|
59
|
+
const stmt = db.prepare('SELECT * FROM tags WHERE name = ?');
|
|
60
|
+
const result = stmt.get(name);
|
|
61
|
+
return result ? result : null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* List all tags
|
|
65
|
+
* @returns Array of tags sorted by creation date in descending order
|
|
66
|
+
*/
|
|
67
|
+
listTags() {
|
|
68
|
+
const db = this.db;
|
|
69
|
+
const stmt = db.prepare('SELECT * FROM tags ORDER BY created_at DESC');
|
|
70
|
+
const results = stmt.all();
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Update tag
|
|
75
|
+
* @param id - Tag ID
|
|
76
|
+
* @param input - Tag update input
|
|
77
|
+
* @returns Updated tag object or null if not found
|
|
78
|
+
* @throws Error if tag name already exists
|
|
79
|
+
*/
|
|
80
|
+
updateTag(id, input) {
|
|
81
|
+
const db = this.db;
|
|
82
|
+
// Verify that tag exists
|
|
83
|
+
const existingTag = this.getTag(id);
|
|
84
|
+
if (!existingTag) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
// Validate name if being updated
|
|
88
|
+
if (input.name !== undefined) {
|
|
89
|
+
if (!input.name || input.name.trim().length === 0) {
|
|
90
|
+
throw new Error('Name is required');
|
|
91
|
+
}
|
|
92
|
+
if (input.name.length > 50) {
|
|
93
|
+
throw new Error('Name must not exceed 50 characters');
|
|
94
|
+
}
|
|
95
|
+
if (/^\d+$/.test(input.name.trim())) {
|
|
96
|
+
throw new Error('Tag name cannot be purely numeric');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Check for duplicate tag name if changing the name
|
|
100
|
+
if (input.name !== undefined && input.name !== existingTag.name) {
|
|
101
|
+
const duplicateTag = this.getTagByName(input.name);
|
|
102
|
+
if (duplicateTag) {
|
|
103
|
+
throw new Error(`Tag with name "${input.name}" already exists`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const stmt = db.prepare(`
|
|
107
|
+
UPDATE tags
|
|
108
|
+
SET name = COALESCE(?, name)
|
|
109
|
+
WHERE id = ?
|
|
110
|
+
`);
|
|
111
|
+
stmt.run(input.name, id);
|
|
112
|
+
return this.getTag(id);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Delete tag
|
|
116
|
+
* @param id - Tag ID
|
|
117
|
+
* @returns True if deletion succeeded, false if tag not found
|
|
118
|
+
*/
|
|
119
|
+
deleteTag(id) {
|
|
120
|
+
const db = this.db;
|
|
121
|
+
const stmt = db.prepare('DELETE FROM tags WHERE id = ?');
|
|
122
|
+
const result = stmt.run(id);
|
|
123
|
+
return result.changes > 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exports.TagService = TagService;
|
|
127
|
+
//# sourceMappingURL=TagService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TagService.js","sourceRoot":"","sources":["../../src/services/TagService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { TaskBlock, CreateTaskBlockInput } from '../models';
|
|
2
|
+
import { TaskService } from './TaskService';
|
|
3
|
+
import Database from 'better-sqlite3';
|
|
4
|
+
/**
|
|
5
|
+
* Task Block Service
|
|
6
|
+
* Manages blocking relationships between tasks
|
|
7
|
+
*/
|
|
8
|
+
export declare class TaskBlockService {
|
|
9
|
+
private db;
|
|
10
|
+
private taskService;
|
|
11
|
+
constructor(db?: Database.Database, taskService?: TaskService);
|
|
12
|
+
/**
|
|
13
|
+
* Add a blocking relationship between tasks
|
|
14
|
+
* @param input - Creation input for the blocking relationship
|
|
15
|
+
* @returns Created blocking relationship
|
|
16
|
+
* @throws Error if tasks do not exist, self-reference is detected, or circular reference would be created
|
|
17
|
+
*/
|
|
18
|
+
addBlock(input: CreateTaskBlockInput): TaskBlock;
|
|
19
|
+
/**
|
|
20
|
+
* Remove a blocking relationship between tasks
|
|
21
|
+
* @param blockerId - ID of the blocking task
|
|
22
|
+
* @param blockedId - ID of the blocked task
|
|
23
|
+
* @returns True if removal was successful, false if relationship was not found
|
|
24
|
+
*/
|
|
25
|
+
removeBlock(blockerId: number, blockedId: number): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Get IDs of tasks blocked by the specified task
|
|
28
|
+
* Returns a list of IDs for tasks that are blocked by the given blocker task
|
|
29
|
+
* @param blockerId - ID of the blocking task
|
|
30
|
+
* @returns Array of IDs for tasks blocked by this task
|
|
31
|
+
*/
|
|
32
|
+
getBlockedTaskIds(blockerId: number): number[];
|
|
33
|
+
/**
|
|
34
|
+
* Get IDs of tasks blocking the specified task
|
|
35
|
+
* Returns a list of IDs for tasks that block the given task
|
|
36
|
+
* @param blockedId - ID of the blocked task
|
|
37
|
+
* @returns Array of IDs for blocking tasks
|
|
38
|
+
*/
|
|
39
|
+
getBlockerTaskIds(blockedId: number): number[];
|
|
40
|
+
/**
|
|
41
|
+
* Check for circular references in blocking relationships (private method)
|
|
42
|
+
* Detects whether adding a new blocking relationship would create a cycle using BFS algorithm
|
|
43
|
+
* @param blockerId - ID of the blocking task
|
|
44
|
+
* @param blockedId - ID of the blocked task
|
|
45
|
+
* @returns True if a circular reference would be created, false otherwise
|
|
46
|
+
*/
|
|
47
|
+
private wouldCreateBlockCycle;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=TaskBlockService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskBlockService.d.ts","sourceRoot":"","sources":["../../src/services/TaskBlockService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,WAAW,CAAc;gBAErB,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,WAAW;IAK7D;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,SAAS;IAuChD;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;IAa1D;;;;;OAKG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAY9C;;;;;OAKG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAY9C;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;CAiB9B"}
|