@agentuity/runtime 2.0.11 → 3.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +37 -65
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -61
- package/dist/index.js.map +1 -1
- package/package.json +9 -38
- package/src/index.ts +58 -259
- package/AGENTS.md +0 -116
- package/dist/_config.d.ts +0 -100
- package/dist/_config.d.ts.map +0 -1
- package/dist/_config.js +0 -147
- package/dist/_config.js.map +0 -1
- package/dist/_context.d.ts +0 -80
- package/dist/_context.d.ts.map +0 -1
- package/dist/_context.js +0 -160
- package/dist/_context.js.map +0 -1
- package/dist/_events.d.ts +0 -64
- package/dist/_events.d.ts.map +0 -1
- package/dist/_events.js +0 -92
- package/dist/_events.js.map +0 -1
- package/dist/_globals.d.ts +0 -58
- package/dist/_globals.d.ts.map +0 -1
- package/dist/_globals.js +0 -71
- package/dist/_globals.js.map +0 -1
- package/dist/_idle.d.ts +0 -7
- package/dist/_idle.d.ts.map +0 -1
- package/dist/_idle.js +0 -10
- package/dist/_idle.js.map +0 -1
- package/dist/_metadata.d.ts +0 -117
- package/dist/_metadata.d.ts.map +0 -1
- package/dist/_metadata.js +0 -268
- package/dist/_metadata.js.map +0 -1
- package/dist/_process-protection.d.ts +0 -27
- package/dist/_process-protection.d.ts.map +0 -1
- package/dist/_process-protection.js +0 -56
- package/dist/_process-protection.js.map +0 -1
- package/dist/_server.d.ts +0 -50
- package/dist/_server.d.ts.map +0 -1
- package/dist/_server.js +0 -89
- package/dist/_server.js.map +0 -1
- package/dist/_services.d.ts +0 -25
- package/dist/_services.d.ts.map +0 -1
- package/dist/_services.js +0 -286
- package/dist/_services.js.map +0 -1
- package/dist/_standalone.d.ts +0 -212
- package/dist/_standalone.d.ts.map +0 -1
- package/dist/_standalone.js +0 -556
- package/dist/_standalone.js.map +0 -1
- package/dist/_tokens.d.ts +0 -12
- package/dist/_tokens.d.ts.map +0 -1
- package/dist/_tokens.js +0 -97
- package/dist/_tokens.js.map +0 -1
- package/dist/_util.d.ts +0 -16
- package/dist/_util.d.ts.map +0 -1
- package/dist/_util.js +0 -54
- package/dist/_util.js.map +0 -1
- package/dist/_validation.d.ts +0 -89
- package/dist/_validation.d.ts.map +0 -1
- package/dist/_validation.js +0 -29
- package/dist/_validation.js.map +0 -1
- package/dist/_waituntil.d.ts +0 -32
- package/dist/_waituntil.d.ts.map +0 -1
- package/dist/_waituntil.js +0 -156
- package/dist/_waituntil.js.map +0 -1
- package/dist/agent.d.ts +0 -1262
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -981
- package/dist/agent.js.map +0 -1
- package/dist/app.d.ts +0 -514
- package/dist/app.d.ts.map +0 -1
- package/dist/app.js +0 -228
- package/dist/app.js.map +0 -1
- package/dist/bootstrap.d.ts +0 -44
- package/dist/bootstrap.d.ts.map +0 -1
- package/dist/bootstrap.js +0 -259
- package/dist/bootstrap.js.map +0 -1
- package/dist/bun-s3-patch.d.ts +0 -37
- package/dist/bun-s3-patch.d.ts.map +0 -1
- package/dist/bun-s3-patch.js +0 -142
- package/dist/bun-s3-patch.js.map +0 -1
- package/dist/cors.d.ts +0 -42
- package/dist/cors.d.ts.map +0 -1
- package/dist/cors.js +0 -117
- package/dist/cors.js.map +0 -1
- package/dist/dev-patches/aisdk.d.ts +0 -17
- package/dist/dev-patches/aisdk.d.ts.map +0 -1
- package/dist/dev-patches/aisdk.js +0 -160
- package/dist/dev-patches/aisdk.js.map +0 -1
- package/dist/dev-patches/gateway.d.ts +0 -16
- package/dist/dev-patches/gateway.d.ts.map +0 -1
- package/dist/dev-patches/gateway.js +0 -54
- package/dist/dev-patches/gateway.js.map +0 -1
- package/dist/dev-patches/index.d.ts +0 -21
- package/dist/dev-patches/index.d.ts.map +0 -1
- package/dist/dev-patches/index.js +0 -33
- package/dist/dev-patches/index.js.map +0 -1
- package/dist/dev-patches/otel-llm.d.ts +0 -12
- package/dist/dev-patches/otel-llm.d.ts.map +0 -1
- package/dist/dev-patches/otel-llm.js +0 -352
- package/dist/dev-patches/otel-llm.js.map +0 -1
- package/dist/devmode.d.ts +0 -3
- package/dist/devmode.d.ts.map +0 -1
- package/dist/devmode.js +0 -167
- package/dist/devmode.js.map +0 -1
- package/dist/eval.d.ts +0 -91
- package/dist/eval.d.ts.map +0 -1
- package/dist/eval.js +0 -16
- package/dist/eval.js.map +0 -1
- package/dist/handlers/_route-meta.d.ts +0 -22
- package/dist/handlers/_route-meta.d.ts.map +0 -1
- package/dist/handlers/_route-meta.js +0 -25
- package/dist/handlers/_route-meta.js.map +0 -1
- package/dist/handlers/cron.d.ts +0 -73
- package/dist/handlers/cron.d.ts.map +0 -1
- package/dist/handlers/cron.js +0 -43
- package/dist/handlers/cron.js.map +0 -1
- package/dist/handlers/index.d.ts +0 -6
- package/dist/handlers/index.d.ts.map +0 -1
- package/dist/handlers/index.js +0 -6
- package/dist/handlers/index.js.map +0 -1
- package/dist/handlers/sse.d.ts +0 -163
- package/dist/handlers/sse.d.ts.map +0 -1
- package/dist/handlers/sse.js +0 -175
- package/dist/handlers/sse.js.map +0 -1
- package/dist/handlers/stream.d.ts +0 -52
- package/dist/handlers/stream.d.ts.map +0 -1
- package/dist/handlers/stream.js +0 -108
- package/dist/handlers/stream.js.map +0 -1
- package/dist/handlers/webrtc.d.ts +0 -49
- package/dist/handlers/webrtc.d.ts.map +0 -1
- package/dist/handlers/webrtc.js +0 -109
- package/dist/handlers/webrtc.js.map +0 -1
- package/dist/handlers/websocket.d.ts +0 -88
- package/dist/handlers/websocket.d.ts.map +0 -1
- package/dist/handlers/websocket.js +0 -161
- package/dist/handlers/websocket.js.map +0 -1
- package/dist/logger/console.d.ts +0 -70
- package/dist/logger/console.d.ts.map +0 -1
- package/dist/logger/console.js +0 -278
- package/dist/logger/console.js.map +0 -1
- package/dist/logger/index.d.ts +0 -3
- package/dist/logger/index.d.ts.map +0 -1
- package/dist/logger/index.js +0 -3
- package/dist/logger/index.js.map +0 -1
- package/dist/logger/internal.d.ts +0 -79
- package/dist/logger/internal.d.ts.map +0 -1
- package/dist/logger/internal.js +0 -133
- package/dist/logger/internal.js.map +0 -1
- package/dist/logger/logger.d.ts +0 -41
- package/dist/logger/logger.d.ts.map +0 -1
- package/dist/logger/logger.js +0 -2
- package/dist/logger/logger.js.map +0 -1
- package/dist/logger/user.d.ts +0 -8
- package/dist/logger/user.d.ts.map +0 -1
- package/dist/logger/user.js +0 -7
- package/dist/logger/user.js.map +0 -1
- package/dist/logger/util.d.ts +0 -11
- package/dist/logger/util.d.ts.map +0 -1
- package/dist/logger/util.js +0 -77
- package/dist/logger/util.js.map +0 -1
- package/dist/middleware.d.ts +0 -105
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js +0 -763
- package/dist/middleware.js.map +0 -1
- package/dist/otel/config.d.ts +0 -19
- package/dist/otel/config.d.ts.map +0 -1
- package/dist/otel/config.js +0 -26
- package/dist/otel/config.js.map +0 -1
- package/dist/otel/console.d.ts +0 -33
- package/dist/otel/console.d.ts.map +0 -1
- package/dist/otel/console.js +0 -86
- package/dist/otel/console.js.map +0 -1
- package/dist/otel/exporters/index.d.ts +0 -4
- package/dist/otel/exporters/index.d.ts.map +0 -1
- package/dist/otel/exporters/index.js +0 -4
- package/dist/otel/exporters/index.js.map +0 -1
- package/dist/otel/exporters/jsonl-log-exporter.d.ts +0 -36
- package/dist/otel/exporters/jsonl-log-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-log-exporter.js +0 -103
- package/dist/otel/exporters/jsonl-log-exporter.js.map +0 -1
- package/dist/otel/exporters/jsonl-metric-exporter.d.ts +0 -40
- package/dist/otel/exporters/jsonl-metric-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-metric-exporter.js +0 -104
- package/dist/otel/exporters/jsonl-metric-exporter.js.map +0 -1
- package/dist/otel/exporters/jsonl-trace-exporter.d.ts +0 -36
- package/dist/otel/exporters/jsonl-trace-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-trace-exporter.js +0 -111
- package/dist/otel/exporters/jsonl-trace-exporter.js.map +0 -1
- package/dist/otel/fetch.d.ts +0 -12
- package/dist/otel/fetch.d.ts.map +0 -1
- package/dist/otel/fetch.js +0 -82
- package/dist/otel/fetch.js.map +0 -1
- package/dist/otel/http.d.ts +0 -16
- package/dist/otel/http.d.ts.map +0 -1
- package/dist/otel/http.js +0 -44
- package/dist/otel/http.js.map +0 -1
- package/dist/otel/logger.d.ts +0 -37
- package/dist/otel/logger.d.ts.map +0 -1
- package/dist/otel/logger.js +0 -265
- package/dist/otel/logger.js.map +0 -1
- package/dist/otel/otel.d.ts +0 -68
- package/dist/otel/otel.d.ts.map +0 -1
- package/dist/otel/otel.js +0 -245
- package/dist/otel/otel.js.map +0 -1
- package/dist/otel/tracestate.d.ts +0 -44
- package/dist/otel/tracestate.d.ts.map +0 -1
- package/dist/otel/tracestate.js +0 -84
- package/dist/otel/tracestate.js.map +0 -1
- package/dist/router.d.ts +0 -66
- package/dist/router.d.ts.map +0 -1
- package/dist/router.js +0 -44
- package/dist/router.js.map +0 -1
- package/dist/services/evalrun/composite.d.ts +0 -21
- package/dist/services/evalrun/composite.d.ts.map +0 -1
- package/dist/services/evalrun/composite.js +0 -26
- package/dist/services/evalrun/composite.js.map +0 -1
- package/dist/services/evalrun/http.d.ts +0 -24
- package/dist/services/evalrun/http.d.ts.map +0 -1
- package/dist/services/evalrun/http.js +0 -115
- package/dist/services/evalrun/http.js.map +0 -1
- package/dist/services/evalrun/index.d.ts +0 -5
- package/dist/services/evalrun/index.d.ts.map +0 -1
- package/dist/services/evalrun/index.js +0 -5
- package/dist/services/evalrun/index.js.map +0 -1
- package/dist/services/evalrun/json.d.ts +0 -21
- package/dist/services/evalrun/json.d.ts.map +0 -1
- package/dist/services/evalrun/json.js +0 -38
- package/dist/services/evalrun/json.js.map +0 -1
- package/dist/services/evalrun/local.d.ts +0 -19
- package/dist/services/evalrun/local.d.ts.map +0 -1
- package/dist/services/evalrun/local.js +0 -22
- package/dist/services/evalrun/local.js.map +0 -1
- package/dist/services/local/_db.d.ts +0 -4
- package/dist/services/local/_db.d.ts.map +0 -1
- package/dist/services/local/_db.js +0 -281
- package/dist/services/local/_db.js.map +0 -1
- package/dist/services/local/_router.d.ts +0 -3
- package/dist/services/local/_router.d.ts.map +0 -1
- package/dist/services/local/_router.js +0 -28
- package/dist/services/local/_router.js.map +0 -1
- package/dist/services/local/_util.d.ts +0 -18
- package/dist/services/local/_util.d.ts.map +0 -1
- package/dist/services/local/_util.js +0 -44
- package/dist/services/local/_util.js.map +0 -1
- package/dist/services/local/email.d.ts +0 -24
- package/dist/services/local/email.d.ts.map +0 -1
- package/dist/services/local/email.js +0 -58
- package/dist/services/local/email.js.map +0 -1
- package/dist/services/local/index.d.ts +0 -10
- package/dist/services/local/index.d.ts.map +0 -1
- package/dist/services/local/index.js +0 -10
- package/dist/services/local/index.js.map +0 -1
- package/dist/services/local/keyvalue.d.ts +0 -17
- package/dist/services/local/keyvalue.d.ts.map +0 -1
- package/dist/services/local/keyvalue.js +0 -133
- package/dist/services/local/keyvalue.js.map +0 -1
- package/dist/services/local/queue.d.ts +0 -10
- package/dist/services/local/queue.d.ts.map +0 -1
- package/dist/services/local/queue.js +0 -96
- package/dist/services/local/queue.js.map +0 -1
- package/dist/services/local/stream.d.ts +0 -12
- package/dist/services/local/stream.d.ts.map +0 -1
- package/dist/services/local/stream.js +0 -266
- package/dist/services/local/stream.js.map +0 -1
- package/dist/services/local/task.d.ts +0 -55
- package/dist/services/local/task.d.ts.map +0 -1
- package/dist/services/local/task.js +0 -1248
- package/dist/services/local/task.js.map +0 -1
- package/dist/services/local/vector.d.ts +0 -17
- package/dist/services/local/vector.d.ts.map +0 -1
- package/dist/services/local/vector.js +0 -303
- package/dist/services/local/vector.js.map +0 -1
- package/dist/services/sandbox/http.d.ts +0 -23
- package/dist/services/sandbox/http.d.ts.map +0 -1
- package/dist/services/sandbox/http.js +0 -327
- package/dist/services/sandbox/http.js.map +0 -1
- package/dist/services/sandbox/index.d.ts +0 -2
- package/dist/services/sandbox/index.d.ts.map +0 -1
- package/dist/services/sandbox/index.js +0 -2
- package/dist/services/sandbox/index.js.map +0 -1
- package/dist/services/session/composite.d.ts +0 -21
- package/dist/services/session/composite.d.ts.map +0 -1
- package/dist/services/session/composite.js +0 -26
- package/dist/services/session/composite.js.map +0 -1
- package/dist/services/session/http.d.ts +0 -34
- package/dist/services/session/http.d.ts.map +0 -1
- package/dist/services/session/http.js +0 -124
- package/dist/services/session/http.js.map +0 -1
- package/dist/services/session/index.d.ts +0 -5
- package/dist/services/session/index.d.ts.map +0 -1
- package/dist/services/session/index.js +0 -5
- package/dist/services/session/index.js.map +0 -1
- package/dist/services/session/json.d.ts +0 -22
- package/dist/services/session/json.d.ts.map +0 -1
- package/dist/services/session/json.js +0 -35
- package/dist/services/session/json.js.map +0 -1
- package/dist/services/session/local.d.ts +0 -19
- package/dist/services/session/local.d.ts.map +0 -1
- package/dist/services/session/local.js +0 -23
- package/dist/services/session/local.js.map +0 -1
- package/dist/services/thread/local.d.ts +0 -20
- package/dist/services/thread/local.d.ts.map +0 -1
- package/dist/services/thread/local.js +0 -158
- package/dist/services/thread/local.js.map +0 -1
- package/dist/session.d.ts +0 -734
- package/dist/session.d.ts.map +0 -1
- package/dist/session.js +0 -1140
- package/dist/session.js.map +0 -1
- package/dist/signature.d.ts +0 -22
- package/dist/signature.d.ts.map +0 -1
- package/dist/signature.js +0 -63
- package/dist/signature.js.map +0 -1
- package/dist/validator.d.ts +0 -142
- package/dist/validator.d.ts.map +0 -1
- package/dist/validator.js +0 -149
- package/dist/validator.js.map +0 -1
- package/dist/version-check.d.ts +0 -20
- package/dist/version-check.d.ts.map +0 -1
- package/dist/version-check.js +0 -157
- package/dist/version-check.js.map +0 -1
- package/dist/web.d.ts +0 -8
- package/dist/web.d.ts.map +0 -1
- package/dist/web.js +0 -67
- package/dist/web.js.map +0 -1
- package/dist/webrtc-signaling.d.ts +0 -80
- package/dist/webrtc-signaling.d.ts.map +0 -1
- package/dist/webrtc-signaling.js +0 -237
- package/dist/webrtc-signaling.js.map +0 -1
- package/dist/workbench.d.ts +0 -17
- package/dist/workbench.d.ts.map +0 -1
- package/dist/workbench.js +0 -605
- package/dist/workbench.js.map +0 -1
- package/src/_config.ts +0 -163
- package/src/_context.ts +0 -240
- package/src/_events.ts +0 -142
- package/src/_globals.ts +0 -92
- package/src/_idle.ts +0 -10
- package/src/_metadata.ts +0 -407
- package/src/_process-protection.ts +0 -71
- package/src/_server.ts +0 -109
- package/src/_services.ts +0 -379
- package/src/_standalone.ts +0 -710
- package/src/_tokens.ts +0 -114
- package/src/_util.ts +0 -62
- package/src/_validation.ts +0 -119
- package/src/_waituntil.ts +0 -188
- package/src/agent.ts +0 -2739
- package/src/app.ts +0 -769
- package/src/bootstrap.ts +0 -321
- package/src/bun-s3-patch.ts +0 -224
- package/src/cors.ts +0 -137
- package/src/dev-patches/aisdk.ts +0 -169
- package/src/dev-patches/gateway.ts +0 -68
- package/src/dev-patches/index.ts +0 -37
- package/src/dev-patches/otel-llm.ts +0 -405
- package/src/devmode.ts +0 -171
- package/src/eval.ts +0 -109
- package/src/globals.d.ts +0 -28
- package/src/handlers/_route-meta.ts +0 -33
- package/src/handlers/cron.ts +0 -141
- package/src/handlers/index.ts +0 -18
- package/src/handlers/sse.ts +0 -358
- package/src/handlers/stream.ts +0 -121
- package/src/handlers/webrtc.ts +0 -125
- package/src/handlers/websocket.ts +0 -203
- package/src/logger/console.ts +0 -323
- package/src/logger/index.ts +0 -2
- package/src/logger/internal.ts +0 -165
- package/src/logger/logger.ts +0 -44
- package/src/logger/user.ts +0 -15
- package/src/logger/util.ts +0 -80
- package/src/middleware.ts +0 -1095
- package/src/otel/config.ts +0 -47
- package/src/otel/console.ts +0 -91
- package/src/otel/exporters/README.md +0 -217
- package/src/otel/exporters/index.ts +0 -3
- package/src/otel/exporters/jsonl-log-exporter.ts +0 -113
- package/src/otel/exporters/jsonl-metric-exporter.ts +0 -120
- package/src/otel/exporters/jsonl-trace-exporter.ts +0 -121
- package/src/otel/fetch.ts +0 -105
- package/src/otel/http.ts +0 -53
- package/src/otel/logger.ts +0 -293
- package/src/otel/otel.ts +0 -354
- package/src/otel/tracestate.ts +0 -108
- package/src/router.ts +0 -75
- package/src/services/evalrun/composite.ts +0 -34
- package/src/services/evalrun/http.ts +0 -167
- package/src/services/evalrun/index.ts +0 -4
- package/src/services/evalrun/json.ts +0 -46
- package/src/services/evalrun/local.ts +0 -28
- package/src/services/local/README.md +0 -1576
- package/src/services/local/_db.ts +0 -353
- package/src/services/local/_router.ts +0 -40
- package/src/services/local/_util.ts +0 -55
- package/src/services/local/email.ts +0 -91
- package/src/services/local/index.ts +0 -9
- package/src/services/local/keyvalue.ts +0 -174
- package/src/services/local/queue.ts +0 -145
- package/src/services/local/stream.ts +0 -358
- package/src/services/local/task.ts +0 -1711
- package/src/services/local/vector.ts +0 -438
- package/src/services/sandbox/http.ts +0 -522
- package/src/services/sandbox/index.ts +0 -1
- package/src/services/session/composite.ts +0 -33
- package/src/services/session/http.ts +0 -167
- package/src/services/session/index.ts +0 -4
- package/src/services/session/json.ts +0 -42
- package/src/services/session/local.ts +0 -33
- package/src/services/thread/local.ts +0 -199
- package/src/session.ts +0 -1960
- package/src/signature.ts +0 -82
- package/src/validator.ts +0 -283
- package/src/version-check.ts +0 -184
- package/src/web.ts +0 -76
- package/src/webrtc-signaling.ts +0 -288
- package/src/workbench.ts +0 -725
|
@@ -1,1248 +0,0 @@
|
|
|
1
|
-
import { StructuredError, normalizeTaskStatus } from '@agentuity/core';
|
|
2
|
-
import { now } from './_util';
|
|
3
|
-
const TaskTitleRequiredError = StructuredError('TaskTitleRequiredError', 'Task title is required and must be a non-empty string');
|
|
4
|
-
const TaskNotFoundError = StructuredError('TaskNotFoundError', 'Task not found');
|
|
5
|
-
const TaskAlreadyClosedError = StructuredError('TaskAlreadyClosedError', 'Task is already closed');
|
|
6
|
-
const CommentNotFoundError = StructuredError('CommentNotFoundError', 'Comment not found');
|
|
7
|
-
const TagNotFoundError = StructuredError('TagNotFoundError', 'Tag not found');
|
|
8
|
-
const CommentBodyRequiredError = StructuredError('CommentBodyRequiredError', 'Comment body is required and must be a non-empty string');
|
|
9
|
-
const CommentUserRequiredError = StructuredError('CommentUserRequiredError', 'Comment user ID is required and must be a non-empty string');
|
|
10
|
-
const TagNameRequiredError = StructuredError('TagNameRequiredError', 'Tag name is required and must be a non-empty string');
|
|
11
|
-
const AttachmentNotSupportedError = StructuredError('AttachmentNotSupportedError', 'Attachments are not supported in local task storage');
|
|
12
|
-
const UserNotFoundError = StructuredError('UserNotFoundError', 'User not found');
|
|
13
|
-
const UserNameRequiredError = StructuredError('UserNameRequiredError', 'User name is required and must be a non-empty string');
|
|
14
|
-
const ProjectNotFoundError = StructuredError('ProjectNotFoundError', 'Project not found');
|
|
15
|
-
const ProjectNameRequiredError = StructuredError('ProjectNameRequiredError', 'Project name is required and must be a non-empty string');
|
|
16
|
-
const DEFAULT_LIMIT = 100;
|
|
17
|
-
const SORT_FIELDS = {
|
|
18
|
-
created_at: 'created_at',
|
|
19
|
-
updated_at: 'updated_at',
|
|
20
|
-
title: 'title',
|
|
21
|
-
priority: 'priority',
|
|
22
|
-
status: 'status',
|
|
23
|
-
type: 'type',
|
|
24
|
-
open_date: 'open_date',
|
|
25
|
-
in_progress_date: 'in_progress_date',
|
|
26
|
-
closed_date: 'closed_date',
|
|
27
|
-
};
|
|
28
|
-
const DURATION_UNITS = {
|
|
29
|
-
s: 1000,
|
|
30
|
-
m: 60 * 1000,
|
|
31
|
-
h: 60 * 60 * 1000,
|
|
32
|
-
d: 24 * 60 * 60 * 1000,
|
|
33
|
-
w: 7 * 24 * 60 * 60 * 1000,
|
|
34
|
-
};
|
|
35
|
-
const InvalidDurationError = StructuredError('InvalidDurationError', 'Invalid duration format: use a number followed by s (seconds), m (minutes), h (hours), d (days), or w (weeks)');
|
|
36
|
-
function parseDurationMs(duration) {
|
|
37
|
-
const match = duration.match(/^(\d+)([smhdw])$/);
|
|
38
|
-
if (!match) {
|
|
39
|
-
throw new InvalidDurationError();
|
|
40
|
-
}
|
|
41
|
-
const value = parseInt(match[1], 10);
|
|
42
|
-
const unit = match[2];
|
|
43
|
-
const ms = DURATION_UNITS[unit];
|
|
44
|
-
if (!ms) {
|
|
45
|
-
throw new InvalidDurationError();
|
|
46
|
-
}
|
|
47
|
-
return value * ms;
|
|
48
|
-
}
|
|
49
|
-
function generateTaskId() {
|
|
50
|
-
return `task_${crypto.randomUUID().replace(/-/g, '').slice(0, 24)}`;
|
|
51
|
-
}
|
|
52
|
-
function generateChangelogId() {
|
|
53
|
-
return `taskch_${crypto.randomUUID().replace(/-/g, '').slice(0, 24)}`;
|
|
54
|
-
}
|
|
55
|
-
function toTask(row) {
|
|
56
|
-
return {
|
|
57
|
-
id: row.id,
|
|
58
|
-
created_at: new Date(row.created_at).toISOString(),
|
|
59
|
-
updated_at: new Date(row.updated_at).toISOString(),
|
|
60
|
-
title: row.title,
|
|
61
|
-
description: row.description ?? undefined,
|
|
62
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
|
|
63
|
-
priority: row.priority,
|
|
64
|
-
parent_id: row.parent_id ?? undefined,
|
|
65
|
-
type: row.type,
|
|
66
|
-
status: row.status,
|
|
67
|
-
open_date: row.open_date ?? undefined,
|
|
68
|
-
in_progress_date: row.in_progress_date ?? undefined,
|
|
69
|
-
closed_date: row.closed_date ?? undefined,
|
|
70
|
-
created_id: row.created_id,
|
|
71
|
-
assigned_id: row.assigned_id ?? undefined,
|
|
72
|
-
closed_id: row.closed_id ?? undefined,
|
|
73
|
-
deleted: row.deleted === 1,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function toComment(row) {
|
|
77
|
-
return {
|
|
78
|
-
id: row.id,
|
|
79
|
-
created_at: new Date(row.created_at).toISOString(),
|
|
80
|
-
updated_at: new Date(row.updated_at).toISOString(),
|
|
81
|
-
task_id: row.task_id,
|
|
82
|
-
user_id: row.user_id,
|
|
83
|
-
body: row.body,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
function toTag(row) {
|
|
87
|
-
return {
|
|
88
|
-
id: row.id,
|
|
89
|
-
created_at: new Date(row.created_at).toISOString(),
|
|
90
|
-
name: row.name,
|
|
91
|
-
color: row.color ?? undefined,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function generateCommentId() {
|
|
95
|
-
return `comment_${crypto.randomUUID().replace(/-/g, '').slice(0, 24)}`;
|
|
96
|
-
}
|
|
97
|
-
function generateTagId() {
|
|
98
|
-
return `tag_${crypto.randomUUID().replace(/-/g, '').slice(0, 24)}`;
|
|
99
|
-
}
|
|
100
|
-
function generateUserId() {
|
|
101
|
-
return `usr_${crypto.randomUUID().replace(/-/g, '').slice(0, 24)}`;
|
|
102
|
-
}
|
|
103
|
-
function generateProjectId() {
|
|
104
|
-
return `prj_${crypto.randomUUID().replace(/-/g, '').slice(0, 24)}`;
|
|
105
|
-
}
|
|
106
|
-
function toChangelogEntry(row) {
|
|
107
|
-
return {
|
|
108
|
-
id: row.id,
|
|
109
|
-
created_at: new Date(row.created_at).toISOString(),
|
|
110
|
-
task_id: row.task_id,
|
|
111
|
-
field: row.field,
|
|
112
|
-
old_value: row.old_value ?? undefined,
|
|
113
|
-
new_value: row.new_value ?? undefined,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
export class LocalTaskStorage {
|
|
117
|
-
#db;
|
|
118
|
-
#projectPath;
|
|
119
|
-
constructor(db, projectPath) {
|
|
120
|
-
this.#db = db;
|
|
121
|
-
this.#projectPath = projectPath;
|
|
122
|
-
}
|
|
123
|
-
async create(params) {
|
|
124
|
-
const trimmedTitle = params?.title?.trim();
|
|
125
|
-
if (!trimmedTitle) {
|
|
126
|
-
throw new TaskTitleRequiredError();
|
|
127
|
-
}
|
|
128
|
-
const id = generateTaskId();
|
|
129
|
-
const timestamp = now();
|
|
130
|
-
const status = params.status ? normalizeTaskStatus(params.status) : 'open';
|
|
131
|
-
const priority = params.priority ?? 'none';
|
|
132
|
-
const openDate = status === 'open' ? new Date(timestamp).toISOString() : null;
|
|
133
|
-
const inProgressDate = status === 'in_progress' ? new Date(timestamp).toISOString() : null;
|
|
134
|
-
const closedDate = status === 'done' ? new Date(timestamp).toISOString() : null;
|
|
135
|
-
const stmt = this.#db.prepare(`
|
|
136
|
-
INSERT INTO task_storage (
|
|
137
|
-
project_path,
|
|
138
|
-
id,
|
|
139
|
-
title,
|
|
140
|
-
description,
|
|
141
|
-
metadata,
|
|
142
|
-
priority,
|
|
143
|
-
parent_id,
|
|
144
|
-
type,
|
|
145
|
-
status,
|
|
146
|
-
open_date,
|
|
147
|
-
in_progress_date,
|
|
148
|
-
closed_date,
|
|
149
|
-
created_id,
|
|
150
|
-
assigned_id,
|
|
151
|
-
closed_id,
|
|
152
|
-
created_at,
|
|
153
|
-
updated_at
|
|
154
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
155
|
-
`);
|
|
156
|
-
const row = {
|
|
157
|
-
id,
|
|
158
|
-
created_at: timestamp,
|
|
159
|
-
updated_at: timestamp,
|
|
160
|
-
title: trimmedTitle,
|
|
161
|
-
description: params.description ?? null,
|
|
162
|
-
metadata: params.metadata ? JSON.stringify(params.metadata) : null,
|
|
163
|
-
priority,
|
|
164
|
-
parent_id: params.parent_id ?? null,
|
|
165
|
-
type: params.type,
|
|
166
|
-
status,
|
|
167
|
-
open_date: openDate,
|
|
168
|
-
in_progress_date: inProgressDate,
|
|
169
|
-
closed_date: closedDate,
|
|
170
|
-
created_id: params.created_id,
|
|
171
|
-
assigned_id: params.assigned_id ?? null,
|
|
172
|
-
closed_id: null,
|
|
173
|
-
deleted: 0,
|
|
174
|
-
};
|
|
175
|
-
stmt.run(this.#projectPath, row.id, row.title, row.description, row.metadata, row.priority, row.parent_id, row.type, row.status, row.open_date, row.in_progress_date, row.closed_date, row.created_id, row.assigned_id, row.closed_id, row.created_at, row.updated_at);
|
|
176
|
-
return toTask(row);
|
|
177
|
-
}
|
|
178
|
-
async get(id) {
|
|
179
|
-
const query = this.#db.query(`
|
|
180
|
-
SELECT
|
|
181
|
-
id,
|
|
182
|
-
created_at,
|
|
183
|
-
updated_at,
|
|
184
|
-
title,
|
|
185
|
-
description,
|
|
186
|
-
metadata,
|
|
187
|
-
priority,
|
|
188
|
-
parent_id,
|
|
189
|
-
type,
|
|
190
|
-
status,
|
|
191
|
-
open_date,
|
|
192
|
-
in_progress_date,
|
|
193
|
-
closed_date,
|
|
194
|
-
created_id,
|
|
195
|
-
assigned_id,
|
|
196
|
-
closed_id,
|
|
197
|
-
deleted
|
|
198
|
-
FROM task_storage
|
|
199
|
-
WHERE project_path = ? AND id = ?
|
|
200
|
-
`);
|
|
201
|
-
const row = query.get(this.#projectPath, id);
|
|
202
|
-
if (!row) {
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
return toTask(row);
|
|
206
|
-
}
|
|
207
|
-
async list(params) {
|
|
208
|
-
const filters = ['project_path = ?'];
|
|
209
|
-
const values = [this.#projectPath];
|
|
210
|
-
if (params?.status) {
|
|
211
|
-
filters.push('status = ?');
|
|
212
|
-
values.push(normalizeTaskStatus(params.status));
|
|
213
|
-
}
|
|
214
|
-
if (params?.type) {
|
|
215
|
-
filters.push('type = ?');
|
|
216
|
-
values.push(params.type);
|
|
217
|
-
}
|
|
218
|
-
if (params?.priority) {
|
|
219
|
-
filters.push('priority = ?');
|
|
220
|
-
values.push(params.priority);
|
|
221
|
-
}
|
|
222
|
-
if (params?.assigned_id) {
|
|
223
|
-
filters.push('assigned_id = ?');
|
|
224
|
-
values.push(params.assigned_id);
|
|
225
|
-
}
|
|
226
|
-
if (params?.parent_id) {
|
|
227
|
-
filters.push('parent_id = ?');
|
|
228
|
-
values.push(params.parent_id);
|
|
229
|
-
}
|
|
230
|
-
if (params?.created_id) {
|
|
231
|
-
filters.push('created_id = ?');
|
|
232
|
-
values.push(params.created_id);
|
|
233
|
-
}
|
|
234
|
-
if (params?.project_id) {
|
|
235
|
-
filters.push('project_id = ?');
|
|
236
|
-
values.push(params.project_id);
|
|
237
|
-
}
|
|
238
|
-
if (params?.deleted === undefined) {
|
|
239
|
-
filters.push('deleted = 0');
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
filters.push('deleted = ?');
|
|
243
|
-
values.push(params.deleted ? 1 : 0);
|
|
244
|
-
}
|
|
245
|
-
if (params?.tag_id) {
|
|
246
|
-
filters.push('id IN (SELECT task_id FROM task_tag_association_storage WHERE tag_id = ? AND project_path = ?)');
|
|
247
|
-
values.push(params.tag_id);
|
|
248
|
-
values.push(this.#projectPath);
|
|
249
|
-
}
|
|
250
|
-
const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
|
|
251
|
-
const sortField = params?.sort && SORT_FIELDS[params.sort] ? SORT_FIELDS[params.sort] : 'created_at';
|
|
252
|
-
const sortOrder = params?.order === 'asc' ? 'ASC' : 'DESC';
|
|
253
|
-
const limit = params?.limit ?? DEFAULT_LIMIT;
|
|
254
|
-
const offset = params?.offset ?? 0;
|
|
255
|
-
const totalQuery = this.#db.query(`SELECT COUNT(*) as count FROM task_storage ${whereClause}`);
|
|
256
|
-
const totalRow = totalQuery.get(...values);
|
|
257
|
-
const query = this.#db.query(`
|
|
258
|
-
SELECT
|
|
259
|
-
id,
|
|
260
|
-
created_at,
|
|
261
|
-
updated_at,
|
|
262
|
-
title,
|
|
263
|
-
description,
|
|
264
|
-
metadata,
|
|
265
|
-
priority,
|
|
266
|
-
parent_id,
|
|
267
|
-
type,
|
|
268
|
-
status,
|
|
269
|
-
open_date,
|
|
270
|
-
in_progress_date,
|
|
271
|
-
closed_date,
|
|
272
|
-
created_id,
|
|
273
|
-
assigned_id,
|
|
274
|
-
closed_id,
|
|
275
|
-
deleted
|
|
276
|
-
FROM task_storage
|
|
277
|
-
${whereClause}
|
|
278
|
-
ORDER BY ${sortField} ${sortOrder}
|
|
279
|
-
LIMIT ? OFFSET ?
|
|
280
|
-
`);
|
|
281
|
-
const rows = query.all(...values, limit, offset);
|
|
282
|
-
return {
|
|
283
|
-
tasks: rows.map(toTask),
|
|
284
|
-
total: totalRow.count,
|
|
285
|
-
limit,
|
|
286
|
-
offset,
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
async update(id, params) {
|
|
290
|
-
const updateInTransaction = this.#db.transaction(() => {
|
|
291
|
-
const existingQuery = this.#db.query(`
|
|
292
|
-
SELECT
|
|
293
|
-
id,
|
|
294
|
-
created_at,
|
|
295
|
-
updated_at,
|
|
296
|
-
title,
|
|
297
|
-
description,
|
|
298
|
-
metadata,
|
|
299
|
-
priority,
|
|
300
|
-
parent_id,
|
|
301
|
-
type,
|
|
302
|
-
status,
|
|
303
|
-
open_date,
|
|
304
|
-
in_progress_date,
|
|
305
|
-
closed_date,
|
|
306
|
-
created_id,
|
|
307
|
-
assigned_id,
|
|
308
|
-
closed_id,
|
|
309
|
-
deleted
|
|
310
|
-
FROM task_storage
|
|
311
|
-
WHERE project_path = ? AND id = ?
|
|
312
|
-
`);
|
|
313
|
-
const existing = existingQuery.get(this.#projectPath, id);
|
|
314
|
-
if (!existing) {
|
|
315
|
-
throw new TaskNotFoundError();
|
|
316
|
-
}
|
|
317
|
-
const trimmedTitle = params.title !== undefined ? params.title?.trim() : undefined;
|
|
318
|
-
if (params.title !== undefined && !trimmedTitle) {
|
|
319
|
-
throw new TaskTitleRequiredError();
|
|
320
|
-
}
|
|
321
|
-
const timestamp = now();
|
|
322
|
-
const nowIso = new Date(timestamp).toISOString();
|
|
323
|
-
const normalizedStatus = params.status ? normalizeTaskStatus(params.status) : undefined;
|
|
324
|
-
const updated = {
|
|
325
|
-
...existing,
|
|
326
|
-
title: trimmedTitle ?? existing.title,
|
|
327
|
-
description: params.description !== undefined ? params.description : existing.description,
|
|
328
|
-
metadata: params.metadata !== undefined
|
|
329
|
-
? params.metadata
|
|
330
|
-
? JSON.stringify(params.metadata)
|
|
331
|
-
: null
|
|
332
|
-
: existing.metadata,
|
|
333
|
-
priority: params.priority ?? existing.priority,
|
|
334
|
-
parent_id: params.parent_id !== undefined ? params.parent_id : existing.parent_id,
|
|
335
|
-
type: params.type ?? existing.type,
|
|
336
|
-
status: normalizedStatus ?? existing.status,
|
|
337
|
-
assigned_id: params.assigned_id !== undefined ? params.assigned_id : existing.assigned_id,
|
|
338
|
-
closed_id: params.closed_id !== undefined ? params.closed_id : existing.closed_id,
|
|
339
|
-
updated_at: timestamp,
|
|
340
|
-
};
|
|
341
|
-
if (normalizedStatus && normalizedStatus !== existing.status) {
|
|
342
|
-
if (normalizedStatus === 'open' && !existing.open_date) {
|
|
343
|
-
updated.open_date = nowIso;
|
|
344
|
-
}
|
|
345
|
-
if (normalizedStatus === 'in_progress' && !existing.in_progress_date) {
|
|
346
|
-
updated.in_progress_date = nowIso;
|
|
347
|
-
}
|
|
348
|
-
if (normalizedStatus === 'done' && !existing.closed_date) {
|
|
349
|
-
updated.closed_date = nowIso;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const changelogEntries = [];
|
|
353
|
-
const compare = (field, oldValue, newValue) => {
|
|
354
|
-
if (oldValue !== newValue) {
|
|
355
|
-
changelogEntries.push({ field, oldValue, newValue });
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
if (params.title !== undefined) {
|
|
359
|
-
compare('title', existing.title, updated.title);
|
|
360
|
-
}
|
|
361
|
-
if (params.description !== undefined) {
|
|
362
|
-
compare('description', existing.description, updated.description);
|
|
363
|
-
}
|
|
364
|
-
if (params.metadata !== undefined) {
|
|
365
|
-
compare('metadata', existing.metadata, updated.metadata);
|
|
366
|
-
}
|
|
367
|
-
if (params.priority !== undefined) {
|
|
368
|
-
compare('priority', existing.priority, updated.priority);
|
|
369
|
-
}
|
|
370
|
-
if (params.parent_id !== undefined) {
|
|
371
|
-
compare('parent_id', existing.parent_id, updated.parent_id);
|
|
372
|
-
}
|
|
373
|
-
if (params.type !== undefined) {
|
|
374
|
-
compare('type', existing.type, updated.type);
|
|
375
|
-
}
|
|
376
|
-
if (normalizedStatus !== undefined) {
|
|
377
|
-
compare('status', existing.status, updated.status);
|
|
378
|
-
}
|
|
379
|
-
if (params.assigned_id !== undefined) {
|
|
380
|
-
compare('assigned_id', existing.assigned_id, updated.assigned_id);
|
|
381
|
-
}
|
|
382
|
-
if (params.closed_id !== undefined) {
|
|
383
|
-
compare('closed_id', existing.closed_id, updated.closed_id);
|
|
384
|
-
}
|
|
385
|
-
const updateStmt = this.#db.prepare(`
|
|
386
|
-
UPDATE task_storage
|
|
387
|
-
SET
|
|
388
|
-
title = ?,
|
|
389
|
-
description = ?,
|
|
390
|
-
metadata = ?,
|
|
391
|
-
priority = ?,
|
|
392
|
-
parent_id = ?,
|
|
393
|
-
type = ?,
|
|
394
|
-
status = ?,
|
|
395
|
-
open_date = ?,
|
|
396
|
-
in_progress_date = ?,
|
|
397
|
-
closed_date = ?,
|
|
398
|
-
created_id = ?,
|
|
399
|
-
assigned_id = ?,
|
|
400
|
-
closed_id = ?,
|
|
401
|
-
updated_at = ?
|
|
402
|
-
WHERE project_path = ? AND id = ?
|
|
403
|
-
`);
|
|
404
|
-
updateStmt.run(updated.title, updated.description, updated.metadata, updated.priority, updated.parent_id, updated.type, updated.status, updated.open_date, updated.in_progress_date, updated.closed_date, updated.created_id, updated.assigned_id, updated.closed_id, updated.updated_at, this.#projectPath, id);
|
|
405
|
-
if (changelogEntries.length > 0) {
|
|
406
|
-
const changelogStmt = this.#db.prepare(`
|
|
407
|
-
INSERT INTO task_changelog_storage (
|
|
408
|
-
project_path,
|
|
409
|
-
id,
|
|
410
|
-
task_id,
|
|
411
|
-
field,
|
|
412
|
-
old_value,
|
|
413
|
-
new_value,
|
|
414
|
-
created_at
|
|
415
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
416
|
-
`);
|
|
417
|
-
for (const entry of changelogEntries) {
|
|
418
|
-
changelogStmt.run(this.#projectPath, generateChangelogId(), id, entry.field, entry.oldValue, entry.newValue, timestamp);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return toTask(updated);
|
|
422
|
-
});
|
|
423
|
-
return updateInTransaction.immediate();
|
|
424
|
-
}
|
|
425
|
-
async close(id) {
|
|
426
|
-
const closeInTransaction = this.#db.transaction(() => {
|
|
427
|
-
const existingQuery = this.#db.query(`
|
|
428
|
-
SELECT
|
|
429
|
-
id,
|
|
430
|
-
created_at,
|
|
431
|
-
updated_at,
|
|
432
|
-
title,
|
|
433
|
-
description,
|
|
434
|
-
metadata,
|
|
435
|
-
priority,
|
|
436
|
-
parent_id,
|
|
437
|
-
type,
|
|
438
|
-
status,
|
|
439
|
-
open_date,
|
|
440
|
-
in_progress_date,
|
|
441
|
-
closed_date,
|
|
442
|
-
created_id,
|
|
443
|
-
assigned_id,
|
|
444
|
-
closed_id,
|
|
445
|
-
deleted
|
|
446
|
-
FROM task_storage
|
|
447
|
-
WHERE project_path = ? AND id = ?
|
|
448
|
-
`);
|
|
449
|
-
const existing = existingQuery.get(this.#projectPath, id);
|
|
450
|
-
if (!existing) {
|
|
451
|
-
throw new TaskNotFoundError();
|
|
452
|
-
}
|
|
453
|
-
if (existing.status === 'done') {
|
|
454
|
-
throw new TaskAlreadyClosedError();
|
|
455
|
-
}
|
|
456
|
-
const timestamp = now();
|
|
457
|
-
const nowIso = new Date(timestamp).toISOString();
|
|
458
|
-
const updated = {
|
|
459
|
-
...existing,
|
|
460
|
-
status: 'done',
|
|
461
|
-
closed_date: existing.closed_date ?? nowIso,
|
|
462
|
-
updated_at: timestamp,
|
|
463
|
-
};
|
|
464
|
-
const updateStmt = this.#db.prepare(`
|
|
465
|
-
UPDATE task_storage
|
|
466
|
-
SET status = ?, closed_date = ?, updated_at = ?
|
|
467
|
-
WHERE project_path = ? AND id = ?
|
|
468
|
-
`);
|
|
469
|
-
updateStmt.run(updated.status, updated.closed_date, updated.updated_at, this.#projectPath, id);
|
|
470
|
-
const changelogStmt = this.#db.prepare(`
|
|
471
|
-
INSERT INTO task_changelog_storage (
|
|
472
|
-
project_path,
|
|
473
|
-
id,
|
|
474
|
-
task_id,
|
|
475
|
-
field,
|
|
476
|
-
old_value,
|
|
477
|
-
new_value,
|
|
478
|
-
created_at
|
|
479
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
480
|
-
`);
|
|
481
|
-
changelogStmt.run(this.#projectPath, generateChangelogId(), id, 'status', existing.status, updated.status, timestamp);
|
|
482
|
-
return toTask(updated);
|
|
483
|
-
});
|
|
484
|
-
return closeInTransaction.immediate();
|
|
485
|
-
}
|
|
486
|
-
async changelog(id, params) {
|
|
487
|
-
const limit = params?.limit ?? DEFAULT_LIMIT;
|
|
488
|
-
const offset = params?.offset ?? 0;
|
|
489
|
-
const totalQuery = this.#db.query(`SELECT COUNT(*) as count FROM task_changelog_storage WHERE project_path = ? AND task_id = ?`);
|
|
490
|
-
const totalRow = totalQuery.get(this.#projectPath, id);
|
|
491
|
-
const query = this.#db.query(`
|
|
492
|
-
SELECT
|
|
493
|
-
id,
|
|
494
|
-
created_at,
|
|
495
|
-
task_id,
|
|
496
|
-
field,
|
|
497
|
-
old_value,
|
|
498
|
-
new_value
|
|
499
|
-
FROM task_changelog_storage
|
|
500
|
-
WHERE project_path = ? AND task_id = ?
|
|
501
|
-
ORDER BY created_at DESC
|
|
502
|
-
LIMIT ? OFFSET ?
|
|
503
|
-
`);
|
|
504
|
-
const rows = query.all(this.#projectPath, id, limit, offset);
|
|
505
|
-
return {
|
|
506
|
-
changelog: rows.map(toChangelogEntry),
|
|
507
|
-
total: totalRow.count,
|
|
508
|
-
limit,
|
|
509
|
-
offset,
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
async softDelete(id) {
|
|
513
|
-
const task = await this.get(id);
|
|
514
|
-
if (!task) {
|
|
515
|
-
throw new TaskNotFoundError();
|
|
516
|
-
}
|
|
517
|
-
const timestamp = now();
|
|
518
|
-
const updateStmt = this.#db.prepare(`
|
|
519
|
-
UPDATE task_storage
|
|
520
|
-
SET status = 'done', deleted = 1, closed_date = COALESCE(closed_date, ?), updated_at = ?
|
|
521
|
-
WHERE project_path = ? AND id = ?
|
|
522
|
-
`);
|
|
523
|
-
updateStmt.run(new Date(timestamp).toISOString(), timestamp, this.#projectPath, id);
|
|
524
|
-
const changelogStmt = this.#db.prepare(`
|
|
525
|
-
INSERT INTO task_changelog_storage (
|
|
526
|
-
project_path, id, task_id, field, old_value, new_value, created_at
|
|
527
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
528
|
-
`);
|
|
529
|
-
changelogStmt.run(this.#projectPath, generateChangelogId(), id, 'deleted', 'false', 'true', timestamp);
|
|
530
|
-
const updated = await this.get(id);
|
|
531
|
-
return updated;
|
|
532
|
-
}
|
|
533
|
-
async batchDelete(params) {
|
|
534
|
-
const conditions = ['project_path = ?', 'deleted = 0'];
|
|
535
|
-
const args = [this.#projectPath];
|
|
536
|
-
if (params.status) {
|
|
537
|
-
conditions.push('status = ?');
|
|
538
|
-
args.push(normalizeTaskStatus(params.status));
|
|
539
|
-
}
|
|
540
|
-
if (params.type) {
|
|
541
|
-
conditions.push('type = ?');
|
|
542
|
-
args.push(params.type);
|
|
543
|
-
}
|
|
544
|
-
if (params.priority) {
|
|
545
|
-
conditions.push('priority = ?');
|
|
546
|
-
args.push(params.priority);
|
|
547
|
-
}
|
|
548
|
-
if (params.parent_id) {
|
|
549
|
-
conditions.push('parent_id = ?');
|
|
550
|
-
args.push(params.parent_id);
|
|
551
|
-
}
|
|
552
|
-
if (params.created_id) {
|
|
553
|
-
conditions.push('created_id = ?');
|
|
554
|
-
args.push(params.created_id);
|
|
555
|
-
}
|
|
556
|
-
if (params.older_than) {
|
|
557
|
-
const ms = parseDurationMs(params.older_than);
|
|
558
|
-
const cutoff = Date.now() - ms;
|
|
559
|
-
conditions.push('created_at < ?');
|
|
560
|
-
args.push(cutoff);
|
|
561
|
-
}
|
|
562
|
-
// Require at least one filter beyond project_path + deleted
|
|
563
|
-
if (conditions.length < 3) {
|
|
564
|
-
const BatchDeleteFilterRequiredError = StructuredError('BatchDeleteFilterRequiredError', 'At least one filter is required for batch delete');
|
|
565
|
-
throw new BatchDeleteFilterRequiredError();
|
|
566
|
-
}
|
|
567
|
-
if (params.limit !== undefined && (!Number.isInteger(params.limit) || params.limit <= 0)) {
|
|
568
|
-
const InvalidBatchDeleteLimitError = StructuredError('InvalidBatchDeleteLimitError', 'Batch delete limit must be a positive integer');
|
|
569
|
-
throw new InvalidBatchDeleteLimitError();
|
|
570
|
-
}
|
|
571
|
-
const limit = Math.min(params.limit ?? 50, 200);
|
|
572
|
-
const whereClause = conditions.join(' AND ');
|
|
573
|
-
const selectQuery = `SELECT id, title FROM task_storage WHERE ${whereClause} ORDER BY created_at ASC LIMIT ?`;
|
|
574
|
-
const selectStmt = this.#db.prepare(selectQuery);
|
|
575
|
-
const rows = selectStmt.all(...args, limit);
|
|
576
|
-
if (rows.length === 0) {
|
|
577
|
-
return { deleted: [], count: 0 };
|
|
578
|
-
}
|
|
579
|
-
const timestamp = now();
|
|
580
|
-
const ids = rows.map((r) => r.id);
|
|
581
|
-
const placeholders = ids.map(() => '?').join(', ');
|
|
582
|
-
const txn = this.#db.transaction(() => {
|
|
583
|
-
const updateStmt = this.#db.prepare(`
|
|
584
|
-
UPDATE task_storage
|
|
585
|
-
SET status = 'done', deleted = 1, closed_date = COALESCE(closed_date, ?), updated_at = ?
|
|
586
|
-
WHERE project_path = ? AND id IN (${placeholders})
|
|
587
|
-
`);
|
|
588
|
-
updateStmt.run(new Date(timestamp).toISOString(), timestamp, this.#projectPath, ...ids);
|
|
589
|
-
const changelogStmt = this.#db.prepare(`
|
|
590
|
-
INSERT INTO task_changelog_storage (
|
|
591
|
-
project_path, id, task_id, field, old_value, new_value, created_at
|
|
592
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
593
|
-
`);
|
|
594
|
-
for (const row of rows) {
|
|
595
|
-
changelogStmt.run(this.#projectPath, generateChangelogId(), row.id, 'deleted', 'false', 'true', timestamp);
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
txn();
|
|
599
|
-
return {
|
|
600
|
-
deleted: rows.map((r) => ({ id: r.id, title: r.title })),
|
|
601
|
-
count: rows.length,
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
async batchUpdate(params) {
|
|
605
|
-
const conditions = ['project_path = ?', 'deleted = 0'];
|
|
606
|
-
const args = [this.#projectPath];
|
|
607
|
-
// Handle explicit IDs
|
|
608
|
-
if (params.ids && params.ids.length > 0) {
|
|
609
|
-
const placeholders = params.ids.map(() => '?').join(', ');
|
|
610
|
-
conditions.push(`id IN (${placeholders})`);
|
|
611
|
-
args.push(...params.ids);
|
|
612
|
-
}
|
|
613
|
-
else {
|
|
614
|
-
// Build filter conditions
|
|
615
|
-
if (params.status) {
|
|
616
|
-
conditions.push('status = ?');
|
|
617
|
-
args.push(normalizeTaskStatus(params.status));
|
|
618
|
-
}
|
|
619
|
-
if (params.type) {
|
|
620
|
-
conditions.push('type = ?');
|
|
621
|
-
args.push(params.type);
|
|
622
|
-
}
|
|
623
|
-
if (params.priority) {
|
|
624
|
-
conditions.push('priority = ?');
|
|
625
|
-
args.push(params.priority);
|
|
626
|
-
}
|
|
627
|
-
if (params.parent_id) {
|
|
628
|
-
conditions.push('parent_id = ?');
|
|
629
|
-
args.push(params.parent_id);
|
|
630
|
-
}
|
|
631
|
-
if (params.created_id) {
|
|
632
|
-
conditions.push('created_id = ?');
|
|
633
|
-
args.push(params.created_id);
|
|
634
|
-
}
|
|
635
|
-
if (params.assigned_id) {
|
|
636
|
-
conditions.push('assigned_id = ?');
|
|
637
|
-
args.push(params.assigned_id);
|
|
638
|
-
}
|
|
639
|
-
if (params.older_than) {
|
|
640
|
-
const ms = parseDurationMs(params.older_than);
|
|
641
|
-
const cutoff = Date.now() - ms;
|
|
642
|
-
conditions.push('created_at < ?');
|
|
643
|
-
args.push(cutoff);
|
|
644
|
-
}
|
|
645
|
-
if (params.project_id) {
|
|
646
|
-
conditions.push('project_id = ?');
|
|
647
|
-
args.push(params.project_id);
|
|
648
|
-
}
|
|
649
|
-
if (params.tag_id) {
|
|
650
|
-
conditions.push('id IN (SELECT task_id FROM task_tag_association_storage WHERE tag_id = ? AND project_path = ?)');
|
|
651
|
-
args.push(params.tag_id);
|
|
652
|
-
args.push(this.#projectPath);
|
|
653
|
-
}
|
|
654
|
-
if (params.newer_than) {
|
|
655
|
-
const ms = parseDurationMs(params.newer_than);
|
|
656
|
-
const cutoff = Date.now() - ms;
|
|
657
|
-
conditions.push('created_at > ?');
|
|
658
|
-
args.push(cutoff);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
// Require at least one filter or IDs
|
|
662
|
-
if (params.ids && params.ids.length > 0) {
|
|
663
|
-
// IDs provided, OK
|
|
664
|
-
}
|
|
665
|
-
else if (conditions.length < 3) {
|
|
666
|
-
throw new Error('At least one filter or ids is required for batch update');
|
|
667
|
-
}
|
|
668
|
-
// Check for update fields
|
|
669
|
-
const hasUpdate = params.new_status ||
|
|
670
|
-
params.new_priority ||
|
|
671
|
-
params.new_assigned_id ||
|
|
672
|
-
params.new_assignee ||
|
|
673
|
-
params.new_title ||
|
|
674
|
-
params.new_description ||
|
|
675
|
-
params.new_metadata ||
|
|
676
|
-
params.new_type;
|
|
677
|
-
if (!hasUpdate) {
|
|
678
|
-
throw new Error('At least one update field is required for batch update');
|
|
679
|
-
}
|
|
680
|
-
if (params.limit !== undefined && (!Number.isInteger(params.limit) || params.limit <= 0)) {
|
|
681
|
-
throw new Error('Batch update limit must be a positive integer');
|
|
682
|
-
}
|
|
683
|
-
const limit = Math.min(params.limit ?? 50, 200);
|
|
684
|
-
const whereClause = conditions.join(' AND ');
|
|
685
|
-
const selectQuery = `SELECT id, title, status, priority FROM task_storage WHERE ${whereClause} ORDER BY created_at ASC LIMIT ?`;
|
|
686
|
-
const selectStmt = this.#db.prepare(selectQuery);
|
|
687
|
-
const rows = selectStmt.all(...args, limit);
|
|
688
|
-
if (rows.length === 0) {
|
|
689
|
-
return { updated: [], count: 0, dry_run: params.dry_run ?? false };
|
|
690
|
-
}
|
|
691
|
-
// Dry run - return preview without updating
|
|
692
|
-
if (params.dry_run) {
|
|
693
|
-
const normalizedStatus = params.new_status
|
|
694
|
-
? normalizeTaskStatus(params.new_status)
|
|
695
|
-
: undefined;
|
|
696
|
-
return {
|
|
697
|
-
updated: rows.map((r) => ({
|
|
698
|
-
id: r.id,
|
|
699
|
-
title: params.new_title ?? r.title,
|
|
700
|
-
status: (normalizedStatus ?? r.status),
|
|
701
|
-
priority: (params.new_priority ?? r.priority),
|
|
702
|
-
})),
|
|
703
|
-
count: rows.length,
|
|
704
|
-
dry_run: true,
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
|
-
const timestamp = now();
|
|
708
|
-
const ids = rows.map((r) => r.id);
|
|
709
|
-
const placeholders = ids.map(() => '?').join(', ');
|
|
710
|
-
const updateFields = ['updated_at = ?'];
|
|
711
|
-
const updateArgs = [timestamp];
|
|
712
|
-
if (params.new_status) {
|
|
713
|
-
updateFields.push('status = ?');
|
|
714
|
-
updateArgs.push(normalizeTaskStatus(params.new_status));
|
|
715
|
-
}
|
|
716
|
-
if (params.new_priority) {
|
|
717
|
-
updateFields.push('priority = ?');
|
|
718
|
-
updateArgs.push(params.new_priority);
|
|
719
|
-
}
|
|
720
|
-
if (params.new_assigned_id) {
|
|
721
|
-
updateFields.push('assigned_id = ?');
|
|
722
|
-
updateArgs.push(params.new_assigned_id);
|
|
723
|
-
}
|
|
724
|
-
if (params.new_title) {
|
|
725
|
-
updateFields.push('title = ?');
|
|
726
|
-
updateArgs.push(params.new_title);
|
|
727
|
-
}
|
|
728
|
-
if (params.new_description) {
|
|
729
|
-
updateFields.push('description = ?');
|
|
730
|
-
updateArgs.push(params.new_description);
|
|
731
|
-
}
|
|
732
|
-
if (params.new_metadata) {
|
|
733
|
-
updateFields.push('metadata = ?');
|
|
734
|
-
updateArgs.push(JSON.stringify(params.new_metadata));
|
|
735
|
-
}
|
|
736
|
-
if (params.new_type) {
|
|
737
|
-
updateFields.push('type = ?');
|
|
738
|
-
updateArgs.push(params.new_type);
|
|
739
|
-
}
|
|
740
|
-
// Set lifecycle timestamps based on new status (only when transitioning)
|
|
741
|
-
if (params.new_status) {
|
|
742
|
-
const newStatus = normalizeTaskStatus(params.new_status);
|
|
743
|
-
if (newStatus === 'open') {
|
|
744
|
-
updateFields.push('open_date = COALESCE(open_date, ?)');
|
|
745
|
-
updateArgs.push(new Date(timestamp).toISOString());
|
|
746
|
-
}
|
|
747
|
-
else if (newStatus === 'in_progress') {
|
|
748
|
-
updateFields.push('in_progress_date = COALESCE(in_progress_date, ?)');
|
|
749
|
-
updateArgs.push(new Date(timestamp).toISOString());
|
|
750
|
-
}
|
|
751
|
-
else if (newStatus === 'done') {
|
|
752
|
-
updateFields.push('closed_date = COALESCE(closed_date, ?)');
|
|
753
|
-
updateArgs.push(new Date(timestamp).toISOString());
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
const txn = this.#db.transaction(() => {
|
|
757
|
-
const updateStmt = this.#db.prepare(`
|
|
758
|
-
UPDATE task_storage SET ${updateFields.join(', ')}
|
|
759
|
-
WHERE project_path = ? AND id IN (${placeholders})
|
|
760
|
-
`);
|
|
761
|
-
updateStmt.run(...updateArgs, this.#projectPath, ...ids);
|
|
762
|
-
const changelogStmt = this.#db.prepare(`
|
|
763
|
-
INSERT INTO task_changelog_storage (
|
|
764
|
-
project_path, id, task_id, field, old_value, new_value, created_at
|
|
765
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
766
|
-
`);
|
|
767
|
-
for (const row of rows) {
|
|
768
|
-
if (params.new_status && row.status !== params.new_status) {
|
|
769
|
-
changelogStmt.run(this.#projectPath, generateChangelogId(), row.id, 'status', row.status, params.new_status, timestamp);
|
|
770
|
-
}
|
|
771
|
-
if (params.new_priority && row.priority !== params.new_priority) {
|
|
772
|
-
changelogStmt.run(this.#projectPath, generateChangelogId(), row.id, 'priority', row.priority, params.new_priority, timestamp);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
});
|
|
776
|
-
txn();
|
|
777
|
-
return {
|
|
778
|
-
updated: rows.map((r) => ({
|
|
779
|
-
id: r.id,
|
|
780
|
-
title: params.new_title ?? r.title,
|
|
781
|
-
status: (params.new_status ?? r.status),
|
|
782
|
-
priority: (params.new_priority ?? r.priority),
|
|
783
|
-
})),
|
|
784
|
-
count: rows.length,
|
|
785
|
-
dry_run: false,
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
async batchClose(params) {
|
|
789
|
-
// Resolve closer ID from either closed_id or closer entity ref
|
|
790
|
-
const closerId = params.closed_id ?? params.closer?.id ?? null;
|
|
791
|
-
const conditions = ['project_path = ?', 'deleted = 0', "status != 'done'"];
|
|
792
|
-
const args = [this.#projectPath];
|
|
793
|
-
// Handle explicit IDs
|
|
794
|
-
if (params.ids && params.ids.length > 0) {
|
|
795
|
-
const placeholders = params.ids.map(() => '?').join(', ');
|
|
796
|
-
conditions.push(`id IN (${placeholders})`);
|
|
797
|
-
args.push(...params.ids);
|
|
798
|
-
}
|
|
799
|
-
else {
|
|
800
|
-
// Build filter conditions
|
|
801
|
-
if (params.status) {
|
|
802
|
-
conditions.push('status = ?');
|
|
803
|
-
args.push(normalizeTaskStatus(params.status));
|
|
804
|
-
}
|
|
805
|
-
if (params.type) {
|
|
806
|
-
conditions.push('type = ?');
|
|
807
|
-
args.push(params.type);
|
|
808
|
-
}
|
|
809
|
-
if (params.priority) {
|
|
810
|
-
conditions.push('priority = ?');
|
|
811
|
-
args.push(params.priority);
|
|
812
|
-
}
|
|
813
|
-
if (params.parent_id) {
|
|
814
|
-
conditions.push('parent_id = ?');
|
|
815
|
-
args.push(params.parent_id);
|
|
816
|
-
}
|
|
817
|
-
if (params.created_id) {
|
|
818
|
-
conditions.push('created_id = ?');
|
|
819
|
-
args.push(params.created_id);
|
|
820
|
-
}
|
|
821
|
-
if (params.assigned_id) {
|
|
822
|
-
conditions.push('assigned_id = ?');
|
|
823
|
-
args.push(params.assigned_id);
|
|
824
|
-
}
|
|
825
|
-
if (params.older_than) {
|
|
826
|
-
const ms = parseDurationMs(params.older_than);
|
|
827
|
-
const cutoff = Date.now() - ms;
|
|
828
|
-
conditions.push('created_at < ?');
|
|
829
|
-
args.push(cutoff);
|
|
830
|
-
}
|
|
831
|
-
if (params.project_id) {
|
|
832
|
-
conditions.push('project_id = ?');
|
|
833
|
-
args.push(params.project_id);
|
|
834
|
-
}
|
|
835
|
-
if (params.tag_id) {
|
|
836
|
-
conditions.push('id IN (SELECT task_id FROM task_tag_association_storage WHERE tag_id = ? AND project_path = ?)');
|
|
837
|
-
args.push(params.tag_id);
|
|
838
|
-
args.push(this.#projectPath);
|
|
839
|
-
}
|
|
840
|
-
if (params.newer_than) {
|
|
841
|
-
const ms = parseDurationMs(params.newer_than);
|
|
842
|
-
const cutoff = Date.now() - ms;
|
|
843
|
-
conditions.push('created_at > ?');
|
|
844
|
-
args.push(cutoff);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
// Require at least one filter or IDs
|
|
848
|
-
if (params.ids && params.ids.length > 0) {
|
|
849
|
-
// IDs provided, OK
|
|
850
|
-
}
|
|
851
|
-
else if (conditions.length < 4) {
|
|
852
|
-
throw new Error('At least one filter or ids is required for batch close');
|
|
853
|
-
}
|
|
854
|
-
if (params.limit !== undefined && (!Number.isInteger(params.limit) || params.limit <= 0)) {
|
|
855
|
-
throw new Error('Batch close limit must be a positive integer');
|
|
856
|
-
}
|
|
857
|
-
const limit = Math.min(params.limit ?? 50, 200);
|
|
858
|
-
const whereClause = conditions.join(' AND ');
|
|
859
|
-
const selectQuery = `SELECT id, title, status FROM task_storage WHERE ${whereClause} ORDER BY created_at ASC LIMIT ?`;
|
|
860
|
-
const selectStmt = this.#db.prepare(selectQuery);
|
|
861
|
-
const rows = selectStmt.all(...args, limit);
|
|
862
|
-
if (rows.length === 0) {
|
|
863
|
-
return { closed: [], count: 0, dry_run: params.dry_run ?? false };
|
|
864
|
-
}
|
|
865
|
-
// Dry run - return preview without closing
|
|
866
|
-
if (params.dry_run) {
|
|
867
|
-
const nowTs = new Date().toISOString();
|
|
868
|
-
return {
|
|
869
|
-
closed: rows.map((r) => ({
|
|
870
|
-
id: r.id,
|
|
871
|
-
title: r.title,
|
|
872
|
-
status: 'done',
|
|
873
|
-
closed_date: nowTs,
|
|
874
|
-
})),
|
|
875
|
-
count: rows.length,
|
|
876
|
-
dry_run: true,
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
const timestamp = now();
|
|
880
|
-
const ids = rows.map((r) => r.id);
|
|
881
|
-
const placeholders = ids.map(() => '?').join(', ');
|
|
882
|
-
const closedDate = new Date(timestamp).toISOString();
|
|
883
|
-
const txn = this.#db.transaction(() => {
|
|
884
|
-
const updateStmt = this.#db.prepare(`
|
|
885
|
-
UPDATE task_storage SET status = 'done', closed_date = COALESCE(closed_date, ?), closed_id = ?, updated_at = ?
|
|
886
|
-
WHERE project_path = ? AND id IN (${placeholders})
|
|
887
|
-
`);
|
|
888
|
-
updateStmt.run(closedDate, closerId, timestamp, this.#projectPath, ...ids);
|
|
889
|
-
const changelogStmt = this.#db.prepare(`
|
|
890
|
-
INSERT INTO task_changelog_storage (
|
|
891
|
-
project_path, id, task_id, field, old_value, new_value, created_at
|
|
892
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
893
|
-
`);
|
|
894
|
-
for (const row of rows) {
|
|
895
|
-
if (row.status !== 'done') {
|
|
896
|
-
changelogStmt.run(this.#projectPath, generateChangelogId(), row.id, 'status', row.status, 'done', timestamp);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
txn();
|
|
901
|
-
return {
|
|
902
|
-
closed: rows.map((r) => ({
|
|
903
|
-
id: r.id,
|
|
904
|
-
title: r.title,
|
|
905
|
-
status: 'done',
|
|
906
|
-
closed_date: closedDate,
|
|
907
|
-
})),
|
|
908
|
-
count: rows.length,
|
|
909
|
-
dry_run: false,
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
async createComment(taskId, body, userId) {
|
|
913
|
-
const trimmedBody = body?.trim();
|
|
914
|
-
if (!trimmedBody) {
|
|
915
|
-
throw new CommentBodyRequiredError();
|
|
916
|
-
}
|
|
917
|
-
const trimmedUserId = userId?.trim();
|
|
918
|
-
if (!trimmedUserId) {
|
|
919
|
-
throw new CommentUserRequiredError();
|
|
920
|
-
}
|
|
921
|
-
const task = await this.get(taskId);
|
|
922
|
-
if (!task) {
|
|
923
|
-
throw new TaskNotFoundError();
|
|
924
|
-
}
|
|
925
|
-
const id = generateCommentId();
|
|
926
|
-
const timestamp = now();
|
|
927
|
-
const stmt = this.#db.prepare(`
|
|
928
|
-
INSERT INTO task_comment_storage (
|
|
929
|
-
project_path, id, task_id, user_id, body, created_at, updated_at
|
|
930
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
931
|
-
`);
|
|
932
|
-
stmt.run(this.#projectPath, id, taskId, trimmedUserId, trimmedBody, timestamp, timestamp);
|
|
933
|
-
return toComment({
|
|
934
|
-
id,
|
|
935
|
-
created_at: timestamp,
|
|
936
|
-
updated_at: timestamp,
|
|
937
|
-
task_id: taskId,
|
|
938
|
-
user_id: trimmedUserId,
|
|
939
|
-
body: trimmedBody,
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
async getComment(commentId) {
|
|
943
|
-
const query = this.#db.query(`
|
|
944
|
-
SELECT id, created_at, updated_at, task_id, user_id, body
|
|
945
|
-
FROM task_comment_storage
|
|
946
|
-
WHERE project_path = ? AND id = ?
|
|
947
|
-
`);
|
|
948
|
-
const row = query.get(this.#projectPath, commentId);
|
|
949
|
-
if (!row) {
|
|
950
|
-
throw new CommentNotFoundError();
|
|
951
|
-
}
|
|
952
|
-
return toComment(row);
|
|
953
|
-
}
|
|
954
|
-
async updateComment(commentId, body) {
|
|
955
|
-
const trimmedBody = body?.trim();
|
|
956
|
-
if (!trimmedBody) {
|
|
957
|
-
throw new CommentBodyRequiredError();
|
|
958
|
-
}
|
|
959
|
-
const existing = await this.getComment(commentId);
|
|
960
|
-
const timestamp = now();
|
|
961
|
-
const stmt = this.#db.prepare(`
|
|
962
|
-
UPDATE task_comment_storage
|
|
963
|
-
SET body = ?, updated_at = ?
|
|
964
|
-
WHERE project_path = ? AND id = ?
|
|
965
|
-
`);
|
|
966
|
-
stmt.run(trimmedBody, timestamp, this.#projectPath, commentId);
|
|
967
|
-
return {
|
|
968
|
-
...existing,
|
|
969
|
-
body: trimmedBody,
|
|
970
|
-
updated_at: new Date(timestamp).toISOString(),
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
async deleteComment(commentId) {
|
|
974
|
-
const stmt = this.#db.prepare(`
|
|
975
|
-
DELETE FROM task_comment_storage
|
|
976
|
-
WHERE project_path = ? AND id = ?
|
|
977
|
-
`);
|
|
978
|
-
stmt.run(this.#projectPath, commentId);
|
|
979
|
-
}
|
|
980
|
-
async listComments(taskId, params) {
|
|
981
|
-
const limit = params?.limit ?? DEFAULT_LIMIT;
|
|
982
|
-
const offset = params?.offset ?? 0;
|
|
983
|
-
const totalQuery = this.#db.query(`SELECT COUNT(*) as count FROM task_comment_storage WHERE project_path = ? AND task_id = ?`);
|
|
984
|
-
const totalRow = totalQuery.get(this.#projectPath, taskId);
|
|
985
|
-
const query = this.#db.query(`
|
|
986
|
-
SELECT id, created_at, updated_at, task_id, user_id, body
|
|
987
|
-
FROM task_comment_storage
|
|
988
|
-
WHERE project_path = ? AND task_id = ?
|
|
989
|
-
ORDER BY created_at DESC
|
|
990
|
-
LIMIT ? OFFSET ?
|
|
991
|
-
`);
|
|
992
|
-
const rows = query.all(this.#projectPath, taskId, limit, offset);
|
|
993
|
-
return {
|
|
994
|
-
comments: rows.map(toComment),
|
|
995
|
-
total: totalRow.count,
|
|
996
|
-
limit,
|
|
997
|
-
offset,
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
async createTag(name, color) {
|
|
1001
|
-
const trimmedName = name?.trim();
|
|
1002
|
-
if (!trimmedName) {
|
|
1003
|
-
throw new TagNameRequiredError();
|
|
1004
|
-
}
|
|
1005
|
-
const id = generateTagId();
|
|
1006
|
-
const timestamp = now();
|
|
1007
|
-
const stmt = this.#db.prepare(`
|
|
1008
|
-
INSERT INTO task_tag_storage (
|
|
1009
|
-
project_path, id, name, color, created_at
|
|
1010
|
-
) VALUES (?, ?, ?, ?, ?)
|
|
1011
|
-
`);
|
|
1012
|
-
stmt.run(this.#projectPath, id, trimmedName, color ?? null, timestamp);
|
|
1013
|
-
return toTag({
|
|
1014
|
-
id,
|
|
1015
|
-
created_at: timestamp,
|
|
1016
|
-
name: trimmedName,
|
|
1017
|
-
color: color ?? null,
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1020
|
-
async getTag(tagId) {
|
|
1021
|
-
const query = this.#db.query(`
|
|
1022
|
-
SELECT id, created_at, name, color
|
|
1023
|
-
FROM task_tag_storage
|
|
1024
|
-
WHERE project_path = ? AND id = ?
|
|
1025
|
-
`);
|
|
1026
|
-
const row = query.get(this.#projectPath, tagId);
|
|
1027
|
-
if (!row) {
|
|
1028
|
-
throw new TagNotFoundError();
|
|
1029
|
-
}
|
|
1030
|
-
return toTag(row);
|
|
1031
|
-
}
|
|
1032
|
-
async updateTag(tagId, name, color) {
|
|
1033
|
-
const trimmedName = name?.trim();
|
|
1034
|
-
if (!trimmedName) {
|
|
1035
|
-
throw new TagNameRequiredError();
|
|
1036
|
-
}
|
|
1037
|
-
// Verify exists
|
|
1038
|
-
await this.getTag(tagId);
|
|
1039
|
-
const stmt = this.#db.prepare(`
|
|
1040
|
-
UPDATE task_tag_storage
|
|
1041
|
-
SET name = ?, color = ?
|
|
1042
|
-
WHERE project_path = ? AND id = ?
|
|
1043
|
-
`);
|
|
1044
|
-
stmt.run(trimmedName, color ?? null, this.#projectPath, tagId);
|
|
1045
|
-
return this.getTag(tagId);
|
|
1046
|
-
}
|
|
1047
|
-
async deleteTag(tagId) {
|
|
1048
|
-
// Also remove tag associations
|
|
1049
|
-
const deleteAssocStmt = this.#db.prepare(`
|
|
1050
|
-
DELETE FROM task_tag_association_storage
|
|
1051
|
-
WHERE project_path = ? AND tag_id = ?
|
|
1052
|
-
`);
|
|
1053
|
-
deleteAssocStmt.run(this.#projectPath, tagId);
|
|
1054
|
-
const stmt = this.#db.prepare(`
|
|
1055
|
-
DELETE FROM task_tag_storage
|
|
1056
|
-
WHERE project_path = ? AND id = ?
|
|
1057
|
-
`);
|
|
1058
|
-
stmt.run(this.#projectPath, tagId);
|
|
1059
|
-
}
|
|
1060
|
-
async listTags() {
|
|
1061
|
-
const query = this.#db.query(`
|
|
1062
|
-
SELECT id, created_at, name, color
|
|
1063
|
-
FROM task_tag_storage
|
|
1064
|
-
WHERE project_path = ?
|
|
1065
|
-
ORDER BY name ASC
|
|
1066
|
-
`);
|
|
1067
|
-
const rows = query.all(this.#projectPath);
|
|
1068
|
-
return {
|
|
1069
|
-
tags: rows.map(toTag),
|
|
1070
|
-
};
|
|
1071
|
-
}
|
|
1072
|
-
async addTagToTask(taskId, tagId) {
|
|
1073
|
-
// Verify task and tag exist
|
|
1074
|
-
const task = await this.get(taskId);
|
|
1075
|
-
if (!task) {
|
|
1076
|
-
throw new TaskNotFoundError();
|
|
1077
|
-
}
|
|
1078
|
-
await this.getTag(tagId);
|
|
1079
|
-
const stmt = this.#db.prepare(`
|
|
1080
|
-
INSERT OR IGNORE INTO task_tag_association_storage (
|
|
1081
|
-
project_path, task_id, tag_id
|
|
1082
|
-
) VALUES (?, ?, ?)
|
|
1083
|
-
`);
|
|
1084
|
-
stmt.run(this.#projectPath, taskId, tagId);
|
|
1085
|
-
}
|
|
1086
|
-
async removeTagFromTask(taskId, tagId) {
|
|
1087
|
-
const stmt = this.#db.prepare(`
|
|
1088
|
-
DELETE FROM task_tag_association_storage
|
|
1089
|
-
WHERE project_path = ? AND task_id = ? AND tag_id = ?
|
|
1090
|
-
`);
|
|
1091
|
-
stmt.run(this.#projectPath, taskId, tagId);
|
|
1092
|
-
}
|
|
1093
|
-
async listTagsForTask(taskId) {
|
|
1094
|
-
const query = this.#db.query(`
|
|
1095
|
-
SELECT t.id, t.created_at, t.name, t.color
|
|
1096
|
-
FROM task_tag_storage t
|
|
1097
|
-
INNER JOIN task_tag_association_storage a ON t.id = a.tag_id AND t.project_path = a.project_path
|
|
1098
|
-
WHERE a.project_path = ? AND a.task_id = ?
|
|
1099
|
-
ORDER BY t.name ASC
|
|
1100
|
-
`);
|
|
1101
|
-
const rows = query.all(this.#projectPath, taskId);
|
|
1102
|
-
return rows.map(toTag);
|
|
1103
|
-
}
|
|
1104
|
-
// Attachment methods — not supported in local storage
|
|
1105
|
-
async uploadAttachment(_taskId, _params) {
|
|
1106
|
-
throw new AttachmentNotSupportedError();
|
|
1107
|
-
}
|
|
1108
|
-
async confirmAttachment(_attachmentId) {
|
|
1109
|
-
throw new AttachmentNotSupportedError();
|
|
1110
|
-
}
|
|
1111
|
-
async downloadAttachment(_attachmentId) {
|
|
1112
|
-
throw new AttachmentNotSupportedError();
|
|
1113
|
-
}
|
|
1114
|
-
async listAttachments(_taskId) {
|
|
1115
|
-
throw new AttachmentNotSupportedError();
|
|
1116
|
-
}
|
|
1117
|
-
async deleteAttachment(_attachmentId) {
|
|
1118
|
-
throw new AttachmentNotSupportedError();
|
|
1119
|
-
}
|
|
1120
|
-
async listUsers() {
|
|
1121
|
-
const query = this.#db.query(`
|
|
1122
|
-
SELECT id, name, type
|
|
1123
|
-
FROM task_user_storage
|
|
1124
|
-
WHERE project_path = ?
|
|
1125
|
-
ORDER BY name ASC
|
|
1126
|
-
`);
|
|
1127
|
-
const rows = query.all(this.#projectPath);
|
|
1128
|
-
return {
|
|
1129
|
-
users: rows.map((row) => ({
|
|
1130
|
-
id: row.id,
|
|
1131
|
-
name: row.name,
|
|
1132
|
-
type: row.type,
|
|
1133
|
-
})),
|
|
1134
|
-
};
|
|
1135
|
-
}
|
|
1136
|
-
async listProjects() {
|
|
1137
|
-
const query = this.#db.query(`
|
|
1138
|
-
SELECT id, name
|
|
1139
|
-
FROM task_project_storage
|
|
1140
|
-
WHERE project_path = ?
|
|
1141
|
-
ORDER BY name ASC
|
|
1142
|
-
`);
|
|
1143
|
-
const rows = query.all(this.#projectPath);
|
|
1144
|
-
return {
|
|
1145
|
-
projects: rows.map((row) => ({
|
|
1146
|
-
id: row.id,
|
|
1147
|
-
name: row.name,
|
|
1148
|
-
})),
|
|
1149
|
-
};
|
|
1150
|
-
}
|
|
1151
|
-
async createUser(params) {
|
|
1152
|
-
const trimmedName = params?.name?.trim();
|
|
1153
|
-
if (!trimmedName) {
|
|
1154
|
-
throw new UserNameRequiredError();
|
|
1155
|
-
}
|
|
1156
|
-
const id = generateUserId();
|
|
1157
|
-
const timestamp = now();
|
|
1158
|
-
const type = params.type ?? 'human';
|
|
1159
|
-
const stmt = this.#db.prepare(`
|
|
1160
|
-
INSERT INTO task_user_storage (
|
|
1161
|
-
project_path, id, name, type, created_at
|
|
1162
|
-
) VALUES (?, ?, ?, ?, ?)
|
|
1163
|
-
`);
|
|
1164
|
-
stmt.run(this.#projectPath, id, trimmedName, type, timestamp);
|
|
1165
|
-
return { id, name: trimmedName, type };
|
|
1166
|
-
}
|
|
1167
|
-
async getUser(userId) {
|
|
1168
|
-
const query = this.#db.query(`
|
|
1169
|
-
SELECT id, name, type
|
|
1170
|
-
FROM task_user_storage
|
|
1171
|
-
WHERE project_path = ? AND id = ?
|
|
1172
|
-
`);
|
|
1173
|
-
const row = query.get(this.#projectPath, userId);
|
|
1174
|
-
if (!row) {
|
|
1175
|
-
throw new UserNotFoundError();
|
|
1176
|
-
}
|
|
1177
|
-
return { id: row.id, name: row.name, type: row.type };
|
|
1178
|
-
}
|
|
1179
|
-
async deleteUser(userId) {
|
|
1180
|
-
const stmt = this.#db.prepare(`
|
|
1181
|
-
DELETE FROM task_user_storage
|
|
1182
|
-
WHERE project_path = ? AND id = ?
|
|
1183
|
-
`);
|
|
1184
|
-
stmt.run(this.#projectPath, userId);
|
|
1185
|
-
}
|
|
1186
|
-
async createProject(params) {
|
|
1187
|
-
const trimmedName = params?.name?.trim();
|
|
1188
|
-
if (!trimmedName) {
|
|
1189
|
-
throw new ProjectNameRequiredError();
|
|
1190
|
-
}
|
|
1191
|
-
const id = generateProjectId();
|
|
1192
|
-
const timestamp = now();
|
|
1193
|
-
const stmt = this.#db.prepare(`
|
|
1194
|
-
INSERT INTO task_project_storage (
|
|
1195
|
-
project_path, id, name, created_at
|
|
1196
|
-
) VALUES (?, ?, ?, ?)
|
|
1197
|
-
`);
|
|
1198
|
-
stmt.run(this.#projectPath, id, trimmedName, timestamp);
|
|
1199
|
-
return { id, name: trimmedName };
|
|
1200
|
-
}
|
|
1201
|
-
async getProject(projectId) {
|
|
1202
|
-
const query = this.#db.query(`
|
|
1203
|
-
SELECT id, name
|
|
1204
|
-
FROM task_project_storage
|
|
1205
|
-
WHERE project_path = ? AND id = ?
|
|
1206
|
-
`);
|
|
1207
|
-
const row = query.get(this.#projectPath, projectId);
|
|
1208
|
-
if (!row) {
|
|
1209
|
-
throw new ProjectNotFoundError();
|
|
1210
|
-
}
|
|
1211
|
-
return { id: row.id, name: row.name };
|
|
1212
|
-
}
|
|
1213
|
-
async deleteProject(projectId) {
|
|
1214
|
-
const stmt = this.#db.prepare(`
|
|
1215
|
-
DELETE FROM task_project_storage
|
|
1216
|
-
WHERE project_path = ? AND id = ?
|
|
1217
|
-
`);
|
|
1218
|
-
stmt.run(this.#projectPath, projectId);
|
|
1219
|
-
}
|
|
1220
|
-
async getActivity(params) {
|
|
1221
|
-
const days = Math.min(365, Math.max(7, params?.days ?? 90));
|
|
1222
|
-
const activity = [];
|
|
1223
|
-
const now = new Date();
|
|
1224
|
-
for (let i = days - 1; i >= 0; i--) {
|
|
1225
|
-
const date = new Date(now);
|
|
1226
|
-
date.setDate(date.getDate() - i);
|
|
1227
|
-
const dateStr = date.toISOString().slice(0, 10);
|
|
1228
|
-
const row = this.#db
|
|
1229
|
-
.prepare(`SELECT
|
|
1230
|
-
COALESCE(SUM(CASE WHEN status = 'open' THEN 1 ELSE 0 END), 0) as open,
|
|
1231
|
-
COALESCE(SUM(CASE WHEN status = 'in_progress' THEN 1 ELSE 0 END), 0) as in_progress,
|
|
1232
|
-
COALESCE(SUM(CASE WHEN status IN ('done', 'closed') THEN 1 ELSE 0 END), 0) as done,
|
|
1233
|
-
COALESCE(SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END), 0) as cancelled
|
|
1234
|
-
FROM task_storage
|
|
1235
|
-
WHERE project_path = ? AND date(created_at) = ?`)
|
|
1236
|
-
.get(this.#projectPath, dateStr);
|
|
1237
|
-
activity.push({
|
|
1238
|
-
date: dateStr,
|
|
1239
|
-
open: row.open,
|
|
1240
|
-
inProgress: row.in_progress,
|
|
1241
|
-
done: row.done,
|
|
1242
|
-
cancelled: row.cancelled,
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
return { activity, days };
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
//# sourceMappingURL=task.js.map
|