vikunja-mcp-ng 0.4.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/LICENSE +21 -0
- package/README.md +123 -0
- package/dist/auth/AuthManager.d.ts +63 -0
- package/dist/auth/AuthManager.d.ts.map +1 -0
- package/dist/auth/AuthManager.js +137 -0
- package/dist/auth/AuthManager.js.map +1 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/permissions.d.ts +60 -0
- package/dist/auth/permissions.d.ts.map +1 -0
- package/dist/auth/permissions.js +173 -0
- package/dist/auth/permissions.js.map +1 -0
- package/dist/client/VikunjaClientFactory.d.ts +41 -0
- package/dist/client/VikunjaClientFactory.d.ts.map +1 -0
- package/dist/client/VikunjaClientFactory.js +58 -0
- package/dist/client/VikunjaClientFactory.js.map +1 -0
- package/dist/client.d.ts +70 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +136 -0
- package/dist/client.js.map +1 -0
- package/dist/config/ConfigurationManager.d.ts +106 -0
- package/dist/config/ConfigurationManager.d.ts.map +1 -0
- package/dist/config/ConfigurationManager.js +509 -0
- package/dist/config/ConfigurationManager.js.map +1 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +36 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/secrets.d.ts +33 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +90 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/config/types.d.ts +1113 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +189 -0
- package/dist/config/types.js.map +1 -0
- package/dist/formatters/BatchImportResponseFormatter.d.ts +89 -0
- package/dist/formatters/BatchImportResponseFormatter.d.ts.map +1 -0
- package/dist/formatters/BatchImportResponseFormatter.js +125 -0
- package/dist/formatters/BatchImportResponseFormatter.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +130 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/direct-middleware.d.ts +9 -0
- package/dist/middleware/direct-middleware.d.ts.map +1 -0
- package/dist/middleware/direct-middleware.js +49 -0
- package/dist/middleware/direct-middleware.js.map +1 -0
- package/dist/middleware/index.d.ts +8 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +21 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/simplified-rate-limit.d.ts +147 -0
- package/dist/middleware/simplified-rate-limit.d.ts.map +1 -0
- package/dist/middleware/simplified-rate-limit.js +533 -0
- package/dist/middleware/simplified-rate-limit.js.map +1 -0
- package/dist/parsers/CSVParser.d.ts +36 -0
- package/dist/parsers/CSVParser.d.ts.map +1 -0
- package/dist/parsers/CSVParser.js +69 -0
- package/dist/parsers/CSVParser.js.map +1 -0
- package/dist/parsers/InputParserFactory.d.ts +17 -0
- package/dist/parsers/InputParserFactory.d.ts.map +1 -0
- package/dist/parsers/InputParserFactory.js +137 -0
- package/dist/parsers/InputParserFactory.js.map +1 -0
- package/dist/parsers/JSONParser.d.ts +74 -0
- package/dist/parsers/JSONParser.d.ts.map +1 -0
- package/dist/parsers/JSONParser.js +69 -0
- package/dist/parsers/JSONParser.js.map +1 -0
- package/dist/services/EntityResolver.d.ts +92 -0
- package/dist/services/EntityResolver.d.ts.map +1 -0
- package/dist/services/EntityResolver.js +201 -0
- package/dist/services/EntityResolver.js.map +1 -0
- package/dist/services/TaskCreationService.d.ts +99 -0
- package/dist/services/TaskCreationService.d.ts.map +1 -0
- package/dist/services/TaskCreationService.js +392 -0
- package/dist/services/TaskCreationService.js.map +1 -0
- package/dist/storage/SimpleFilterStorage.d.ts +103 -0
- package/dist/storage/SimpleFilterStorage.d.ts.map +1 -0
- package/dist/storage/SimpleFilterStorage.js +350 -0
- package/dist/storage/SimpleFilterStorage.js.map +1 -0
- package/dist/storage/filtering/FilterSerializer.d.ts +45 -0
- package/dist/storage/filtering/FilterSerializer.d.ts.map +1 -0
- package/dist/storage/filtering/FilterSerializer.js +171 -0
- package/dist/storage/filtering/FilterSerializer.js.map +1 -0
- package/dist/storage/filtering/FilterValidator.d.ts +59 -0
- package/dist/storage/filtering/FilterValidator.d.ts.map +1 -0
- package/dist/storage/filtering/FilterValidator.js +183 -0
- package/dist/storage/filtering/FilterValidator.js.map +1 -0
- package/dist/storage/index.d.ts +61 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +125 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/templateFileStore.d.ts +62 -0
- package/dist/storage/templateFileStore.d.ts.map +1 -0
- package/dist/storage/templateFileStore.js +151 -0
- package/dist/storage/templateFileStore.js.map +1 -0
- package/dist/tools/admin.d.ts +51 -0
- package/dist/tools/admin.d.ts.map +1 -0
- package/dist/tools/admin.js +207 -0
- package/dist/tools/admin.js.map +1 -0
- package/dist/tools/auth.d.ts +9 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +175 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/batch-import.d.ts +5 -0
- package/dist/tools/batch-import.d.ts.map +1 -0
- package/dist/tools/batch-import.js +153 -0
- package/dist/tools/batch-import.js.map +1 -0
- package/dist/tools/caldav-tokens.d.ts +45 -0
- package/dist/tools/caldav-tokens.d.ts.map +1 -0
- package/dist/tools/caldav-tokens.js +93 -0
- package/dist/tools/caldav-tokens.js.map +1 -0
- package/dist/tools/export.d.ts +14 -0
- package/dist/tools/export.d.ts.map +1 -0
- package/dist/tools/export.js +252 -0
- package/dist/tools/export.js.map +1 -0
- package/dist/tools/filters.d.ts +50 -0
- package/dist/tools/filters.d.ts.map +1 -0
- package/dist/tools/filters.js +512 -0
- package/dist/tools/filters.js.map +1 -0
- package/dist/tools/index.d.ts +53 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +217 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/labels.d.ts +22 -0
- package/dist/tools/labels.d.ts.map +1 -0
- package/dist/tools/labels.js +206 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/notifications.d.ts +17 -0
- package/dist/tools/notifications.d.ts.map +1 -0
- package/dist/tools/notifications.js +171 -0
- package/dist/tools/notifications.js.map +1 -0
- package/dist/tools/projects/backgrounds.d.ts +80 -0
- package/dist/tools/projects/backgrounds.d.ts.map +1 -0
- package/dist/tools/projects/backgrounds.js +154 -0
- package/dist/tools/projects/backgrounds.js.map +1 -0
- package/dist/tools/projects/buckets.d.ts +147 -0
- package/dist/tools/projects/buckets.d.ts.map +1 -0
- package/dist/tools/projects/buckets.js +291 -0
- package/dist/tools/projects/buckets.js.map +1 -0
- package/dist/tools/projects/crud.d.ts +145 -0
- package/dist/tools/projects/crud.d.ts.map +1 -0
- package/dist/tools/projects/crud.js +425 -0
- package/dist/tools/projects/crud.js.map +1 -0
- package/dist/tools/projects/duplicate.d.ts +41 -0
- package/dist/tools/projects/duplicate.d.ts.map +1 -0
- package/dist/tools/projects/duplicate.js +49 -0
- package/dist/tools/projects/duplicate.js.map +1 -0
- package/dist/tools/projects/hierarchy.d.ts +77 -0
- package/dist/tools/projects/hierarchy.d.ts.map +1 -0
- package/dist/tools/projects/hierarchy.js +300 -0
- package/dist/tools/projects/hierarchy.js.map +1 -0
- package/dist/tools/projects/index.d.ts +34 -0
- package/dist/tools/projects/index.d.ts.map +1 -0
- package/dist/tools/projects/index.js +522 -0
- package/dist/tools/projects/index.js.map +1 -0
- package/dist/tools/projects/permission.d.ts +26 -0
- package/dist/tools/projects/permission.d.ts.map +1 -0
- package/dist/tools/projects/permission.js +53 -0
- package/dist/tools/projects/permission.js.map +1 -0
- package/dist/tools/projects/response-formatter.d.ts +54 -0
- package/dist/tools/projects/response-formatter.d.ts.map +1 -0
- package/dist/tools/projects/response-formatter.js +139 -0
- package/dist/tools/projects/response-formatter.js.map +1 -0
- package/dist/tools/projects/sharing-access.d.ts +165 -0
- package/dist/tools/projects/sharing-access.d.ts.map +1 -0
- package/dist/tools/projects/sharing-access.js +450 -0
- package/dist/tools/projects/sharing-access.js.map +1 -0
- package/dist/tools/projects/sharing.d.ts +105 -0
- package/dist/tools/projects/sharing.d.ts.map +1 -0
- package/dist/tools/projects/sharing.js +258 -0
- package/dist/tools/projects/sharing.js.map +1 -0
- package/dist/tools/projects/validation.d.ts +51 -0
- package/dist/tools/projects/validation.d.ts.map +1 -0
- package/dist/tools/projects/validation.js +160 -0
- package/dist/tools/projects/validation.js.map +1 -0
- package/dist/tools/projects/views.d.ts +161 -0
- package/dist/tools/projects/views.d.ts.map +1 -0
- package/dist/tools/projects/views.js +234 -0
- package/dist/tools/projects/views.js.map +1 -0
- package/dist/tools/projects.d.ts +20 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +66 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/reactions.d.ts +25 -0
- package/dist/tools/reactions.d.ts.map +1 -0
- package/dist/tools/reactions.js +108 -0
- package/dist/tools/reactions.js.map +1 -0
- package/dist/tools/subscriptions.d.ts +23 -0
- package/dist/tools/subscriptions.d.ts.map +1 -0
- package/dist/tools/subscriptions.js +111 -0
- package/dist/tools/subscriptions.js.map +1 -0
- package/dist/tools/task-assignees.d.ts +13 -0
- package/dist/tools/task-assignees.d.ts.map +1 -0
- package/dist/tools/task-assignees.js +74 -0
- package/dist/tools/task-assignees.js.map +1 -0
- package/dist/tools/task-bulk.d.ts +13 -0
- package/dist/tools/task-bulk.d.ts.map +1 -0
- package/dist/tools/task-bulk.js +117 -0
- package/dist/tools/task-bulk.js.map +1 -0
- package/dist/tools/task-comments.d.ts +12 -0
- package/dist/tools/task-comments.d.ts.map +1 -0
- package/dist/tools/task-comments.js +65 -0
- package/dist/tools/task-comments.js.map +1 -0
- package/dist/tools/task-crud.d.ts +13 -0
- package/dist/tools/task-crud.d.ts.map +1 -0
- package/dist/tools/task-crud.js +168 -0
- package/dist/tools/task-crud.js.map +1 -0
- package/dist/tools/task-labels.d.ts +13 -0
- package/dist/tools/task-labels.d.ts.map +1 -0
- package/dist/tools/task-labels.js +64 -0
- package/dist/tools/task-labels.js.map +1 -0
- package/dist/tools/task-relations.d.ts +13 -0
- package/dist/tools/task-relations.d.ts.map +1 -0
- package/dist/tools/task-relations.js +74 -0
- package/dist/tools/task-relations.js.map +1 -0
- package/dist/tools/task-reminders.d.ts +13 -0
- package/dist/tools/task-reminders.d.ts.map +1 -0
- package/dist/tools/task-reminders.js +69 -0
- package/dist/tools/task-reminders.js.map +1 -0
- package/dist/tools/tasks/assignees/AssigneeOperationsService.d.ts +63 -0
- package/dist/tools/tasks/assignees/AssigneeOperationsService.d.ts.map +1 -0
- package/dist/tools/tasks/assignees/AssigneeOperationsService.js +152 -0
- package/dist/tools/tasks/assignees/AssigneeOperationsService.js.map +1 -0
- package/dist/tools/tasks/assignees/AssigneeResponseFormatter.d.ts +28 -0
- package/dist/tools/tasks/assignees/AssigneeResponseFormatter.d.ts.map +1 -0
- package/dist/tools/tasks/assignees/AssigneeResponseFormatter.js +73 -0
- package/dist/tools/tasks/assignees/AssigneeResponseFormatter.js.map +1 -0
- package/dist/tools/tasks/assignees/AssigneeValidationService.d.ts +46 -0
- package/dist/tools/tasks/assignees/AssigneeValidationService.d.ts.map +1 -0
- package/dist/tools/tasks/assignees/AssigneeValidationService.js +70 -0
- package/dist/tools/tasks/assignees/AssigneeValidationService.js.map +1 -0
- package/dist/tools/tasks/assignees/index.d.ts +52 -0
- package/dist/tools/tasks/assignees/index.d.ts.map +1 -0
- package/dist/tools/tasks/assignees/index.js +103 -0
- package/dist/tools/tasks/assignees/index.js.map +1 -0
- package/dist/tools/tasks/attach.d.ts +39 -0
- package/dist/tools/tasks/attach.d.ts.map +1 -0
- package/dist/tools/tasks/attach.js +90 -0
- package/dist/tools/tasks/attach.js.map +1 -0
- package/dist/tools/tasks/attachments.d.ts +67 -0
- package/dist/tools/tasks/attachments.d.ts.map +1 -0
- package/dist/tools/tasks/attachments.js +153 -0
- package/dist/tools/tasks/attachments.js.map +1 -0
- package/dist/tools/tasks/buckets.d.ts +41 -0
- package/dist/tools/tasks/buckets.d.ts.map +1 -0
- package/dist/tools/tasks/buckets.js +75 -0
- package/dist/tools/tasks/buckets.js.map +1 -0
- package/dist/tools/tasks/bulk/BatchProcessorFactory.d.ts +30 -0
- package/dist/tools/tasks/bulk/BatchProcessorFactory.d.ts.map +1 -0
- package/dist/tools/tasks/bulk/BatchProcessorFactory.js +69 -0
- package/dist/tools/tasks/bulk/BatchProcessorFactory.js.map +1 -0
- package/dist/tools/tasks/bulk/BulkOperationErrorHandler.d.ts +50 -0
- package/dist/tools/tasks/bulk/BulkOperationErrorHandler.d.ts.map +1 -0
- package/dist/tools/tasks/bulk/BulkOperationErrorHandler.js +183 -0
- package/dist/tools/tasks/bulk/BulkOperationErrorHandler.js.map +1 -0
- package/dist/tools/tasks/bulk/BulkOperationProcessor.d.ts +101 -0
- package/dist/tools/tasks/bulk/BulkOperationProcessor.d.ts.map +1 -0
- package/dist/tools/tasks/bulk/BulkOperationProcessor.js +416 -0
- package/dist/tools/tasks/bulk/BulkOperationProcessor.js.map +1 -0
- package/dist/tools/tasks/bulk/BulkOperationTypes.d.ts +50 -0
- package/dist/tools/tasks/bulk/BulkOperationTypes.d.ts.map +1 -0
- package/dist/tools/tasks/bulk/BulkOperationTypes.js +6 -0
- package/dist/tools/tasks/bulk/BulkOperationTypes.js.map +1 -0
- package/dist/tools/tasks/bulk/BulkOperationValidator.d.ts +53 -0
- package/dist/tools/tasks/bulk/BulkOperationValidator.d.ts.map +1 -0
- package/dist/tools/tasks/bulk/BulkOperationValidator.js +216 -0
- package/dist/tools/tasks/bulk/BulkOperationValidator.js.map +1 -0
- package/dist/tools/tasks/bulk/index.d.ts +9 -0
- package/dist/tools/tasks/bulk/index.d.ts.map +1 -0
- package/dist/tools/tasks/bulk/index.js +15 -0
- package/dist/tools/tasks/bulk/index.js.map +1 -0
- package/dist/tools/tasks/bulk-operations-simplified.d.ts +62 -0
- package/dist/tools/tasks/bulk-operations-simplified.d.ts.map +1 -0
- package/dist/tools/tasks/bulk-operations-simplified.js +416 -0
- package/dist/tools/tasks/bulk-operations-simplified.js.map +1 -0
- package/dist/tools/tasks/bulk-operations.d.ts +8 -0
- package/dist/tools/tasks/bulk-operations.d.ts.map +1 -0
- package/dist/tools/tasks/bulk-operations.js +13 -0
- package/dist/tools/tasks/bulk-operations.js.map +1 -0
- package/dist/tools/tasks/by-index.d.ts +36 -0
- package/dist/tools/tasks/by-index.d.ts.map +1 -0
- package/dist/tools/tasks/by-index.js +52 -0
- package/dist/tools/tasks/by-index.js.map +1 -0
- package/dist/tools/tasks/comments/CommentOperationsService.d.ts +44 -0
- package/dist/tools/tasks/comments/CommentOperationsService.d.ts.map +1 -0
- package/dist/tools/tasks/comments/CommentOperationsService.js +86 -0
- package/dist/tools/tasks/comments/CommentOperationsService.js.map +1 -0
- package/dist/tools/tasks/comments/CommentResponseFormatter.d.ts +41 -0
- package/dist/tools/tasks/comments/CommentResponseFormatter.d.ts.map +1 -0
- package/dist/tools/tasks/comments/CommentResponseFormatter.js +114 -0
- package/dist/tools/tasks/comments/CommentResponseFormatter.js.map +1 -0
- package/dist/tools/tasks/comments/CommentValidationService.d.ts +62 -0
- package/dist/tools/tasks/comments/CommentValidationService.d.ts.map +1 -0
- package/dist/tools/tasks/comments/CommentValidationService.js +105 -0
- package/dist/tools/tasks/comments/CommentValidationService.js.map +1 -0
- package/dist/tools/tasks/comments/index.d.ts +66 -0
- package/dist/tools/tasks/comments/index.d.ts.map +1 -0
- package/dist/tools/tasks/comments/index.js +99 -0
- package/dist/tools/tasks/comments/index.js.map +1 -0
- package/dist/tools/tasks/constants.d.ts +19 -0
- package/dist/tools/tasks/constants.d.ts.map +1 -0
- package/dist/tools/tasks/constants.js +82 -0
- package/dist/tools/tasks/constants.js.map +1 -0
- package/dist/tools/tasks/crud/TaskCreationService.d.ts +29 -0
- package/dist/tools/tasks/crud/TaskCreationService.d.ts.map +1 -0
- package/dist/tools/tasks/crud/TaskCreationService.js +274 -0
- package/dist/tools/tasks/crud/TaskCreationService.js.map +1 -0
- package/dist/tools/tasks/crud/TaskDeletionService.d.ts +19 -0
- package/dist/tools/tasks/crud/TaskDeletionService.d.ts.map +1 -0
- package/dist/tools/tasks/crud/TaskDeletionService.js +96 -0
- package/dist/tools/tasks/crud/TaskDeletionService.js.map +1 -0
- package/dist/tools/tasks/crud/TaskReadService.d.ts +19 -0
- package/dist/tools/tasks/crud/TaskReadService.d.ts.map +1 -0
- package/dist/tools/tasks/crud/TaskReadService.js +66 -0
- package/dist/tools/tasks/crud/TaskReadService.js.map +1 -0
- package/dist/tools/tasks/crud/TaskResponseFormatter.d.ts +18 -0
- package/dist/tools/tasks/crud/TaskResponseFormatter.d.ts.map +1 -0
- package/dist/tools/tasks/crud/TaskResponseFormatter.js +248 -0
- package/dist/tools/tasks/crud/TaskResponseFormatter.js.map +1 -0
- package/dist/tools/tasks/crud/TaskUpdateService.d.ts +33 -0
- package/dist/tools/tasks/crud/TaskUpdateService.d.ts.map +1 -0
- package/dist/tools/tasks/crud/TaskUpdateService.js +294 -0
- package/dist/tools/tasks/crud/TaskUpdateService.js.map +1 -0
- package/dist/tools/tasks/crud/index.d.ts +17 -0
- package/dist/tools/tasks/crud/index.d.ts.map +1 -0
- package/dist/tools/tasks/crud/index.js +20 -0
- package/dist/tools/tasks/crud/index.js.map +1 -0
- package/dist/tools/tasks/duplicate.d.ts +28 -0
- package/dist/tools/tasks/duplicate.d.ts.map +1 -0
- package/dist/tools/tasks/duplicate.js +41 -0
- package/dist/tools/tasks/duplicate.js.map +1 -0
- package/dist/tools/tasks/filtering/FilterExecutor.d.ts +39 -0
- package/dist/tools/tasks/filtering/FilterExecutor.d.ts.map +1 -0
- package/dist/tools/tasks/filtering/FilterExecutor.js +214 -0
- package/dist/tools/tasks/filtering/FilterExecutor.js.map +1 -0
- package/dist/tools/tasks/filtering/FilterValidator.d.ts +59 -0
- package/dist/tools/tasks/filtering/FilterValidator.d.ts.map +1 -0
- package/dist/tools/tasks/filtering/FilterValidator.js +257 -0
- package/dist/tools/tasks/filtering/FilterValidator.js.map +1 -0
- package/dist/tools/tasks/filtering/TaskFilteringOrchestrator.d.ts +78 -0
- package/dist/tools/tasks/filtering/TaskFilteringOrchestrator.d.ts.map +1 -0
- package/dist/tools/tasks/filtering/TaskFilteringOrchestrator.js +195 -0
- package/dist/tools/tasks/filtering/TaskFilteringOrchestrator.js.map +1 -0
- package/dist/tools/tasks/filtering/evaluators.d.ts +41 -0
- package/dist/tools/tasks/filtering/evaluators.d.ts.map +1 -0
- package/dist/tools/tasks/filtering/evaluators.js +224 -0
- package/dist/tools/tasks/filtering/evaluators.js.map +1 -0
- package/dist/tools/tasks/filtering/index.d.ts +11 -0
- package/dist/tools/tasks/filtering/index.d.ts.map +1 -0
- package/dist/tools/tasks/filtering/index.js +26 -0
- package/dist/tools/tasks/filtering/index.js.map +1 -0
- package/dist/tools/tasks/index.d.ts +9 -0
- package/dist/tools/tasks/index.d.ts.map +1 -0
- package/dist/tools/tasks/index.js +337 -0
- package/dist/tools/tasks/index.js.map +1 -0
- package/dist/tools/tasks/labels.d.ts +49 -0
- package/dist/tools/tasks/labels.d.ts.map +1 -0
- package/dist/tools/tasks/labels.js +223 -0
- package/dist/tools/tasks/labels.js.map +1 -0
- package/dist/tools/tasks/mark-read.d.ts +27 -0
- package/dist/tools/tasks/mark-read.d.ts.map +1 -0
- package/dist/tools/tasks/mark-read.js +38 -0
- package/dist/tools/tasks/mark-read.js.map +1 -0
- package/dist/tools/tasks/position.d.ts +53 -0
- package/dist/tools/tasks/position.d.ts.map +1 -0
- package/dist/tools/tasks/position.js +83 -0
- package/dist/tools/tasks/position.js.map +1 -0
- package/dist/tools/tasks/reminders.d.ts +56 -0
- package/dist/tools/tasks/reminders.d.ts.map +1 -0
- package/dist/tools/tasks/reminders.js +213 -0
- package/dist/tools/tasks/reminders.js.map +1 -0
- package/dist/tools/tasks/subtasks.d.ts +85 -0
- package/dist/tools/tasks/subtasks.d.ts.map +1 -0
- package/dist/tools/tasks/subtasks.js +286 -0
- package/dist/tools/tasks/subtasks.js.map +1 -0
- package/dist/tools/tasks/types/filters.d.ts +136 -0
- package/dist/tools/tasks/types/filters.d.ts.map +1 -0
- package/dist/tools/tasks/types/filters.js +7 -0
- package/dist/tools/tasks/types/filters.js.map +1 -0
- package/dist/tools/tasks/validation.d.ts +47 -0
- package/dist/tools/tasks/validation.d.ts.map +1 -0
- package/dist/tools/tasks/validation.js +131 -0
- package/dist/tools/tasks/validation.js.map +1 -0
- package/dist/tools/tasks-relations.d.ts +25 -0
- package/dist/tools/tasks-relations.d.ts.map +1 -0
- package/dist/tools/tasks-relations.js +271 -0
- package/dist/tools/tasks-relations.js.map +1 -0
- package/dist/tools/tasks.d.ts +6 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +10 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/teams.d.ts +9 -0
- package/dist/tools/teams.d.ts.map +1 -0
- package/dist/tools/teams.js +255 -0
- package/dist/tools/teams.js.map +1 -0
- package/dist/tools/templates.d.ts +9 -0
- package/dist/tools/templates.d.ts.map +1 -0
- package/dist/tools/templates.js +450 -0
- package/dist/tools/templates.js.map +1 -0
- package/dist/tools/tokens.d.ts +37 -0
- package/dist/tools/tokens.d.ts.map +1 -0
- package/dist/tools/tokens.js +122 -0
- package/dist/tools/tokens.js.map +1 -0
- package/dist/tools/user-deletion.d.ts +41 -0
- package/dist/tools/user-deletion.d.ts.map +1 -0
- package/dist/tools/user-deletion.js +126 -0
- package/dist/tools/user-deletion.js.map +1 -0
- package/dist/tools/users.d.ts +9 -0
- package/dist/tools/users.d.ts.map +1 -0
- package/dist/tools/users.js +418 -0
- package/dist/tools/users.js.map +1 -0
- package/dist/tools/webhooks.d.ts +31 -0
- package/dist/tools/webhooks.d.ts.map +1 -0
- package/dist/tools/webhooks.js +414 -0
- package/dist/tools/webhooks.js.map +1 -0
- package/dist/transforms/base.d.ts +204 -0
- package/dist/transforms/base.d.ts.map +1 -0
- package/dist/transforms/base.js +175 -0
- package/dist/transforms/base.js.map +1 -0
- package/dist/transforms/field-selector.d.ts +27 -0
- package/dist/transforms/field-selector.d.ts.map +1 -0
- package/dist/transforms/field-selector.js +91 -0
- package/dist/transforms/field-selector.js.map +1 -0
- package/dist/transforms/index.d.ts +15 -0
- package/dist/transforms/index.d.ts.map +1 -0
- package/dist/transforms/index.js +50 -0
- package/dist/transforms/index.js.map +1 -0
- package/dist/transforms/size-calculator.d.ts +131 -0
- package/dist/transforms/size-calculator.d.ts.map +1 -0
- package/dist/transforms/size-calculator.js +284 -0
- package/dist/transforms/size-calculator.js.map +1 -0
- package/dist/transforms/task.d.ts +207 -0
- package/dist/transforms/task.d.ts.map +1 -0
- package/dist/transforms/task.js +259 -0
- package/dist/transforms/task.js.map +1 -0
- package/dist/types/errors.d.ts +65 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +44 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/filters.d.ts +149 -0
- package/dist/types/filters.d.ts.map +1 -0
- package/dist/types/filters.js +26 -0
- package/dist/types/filters.js.map +1 -0
- package/dist/types/index.d.ts +147 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +24 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/node-vikunja-extended.d.ts +36 -0
- package/dist/types/node-vikunja-extended.d.ts.map +1 -0
- package/dist/types/node-vikunja-extended.js +25 -0
- package/dist/types/node-vikunja-extended.js.map +1 -0
- package/dist/types/responses.d.ts +121 -0
- package/dist/types/responses.d.ts.map +1 -0
- package/dist/types/responses.js +26 -0
- package/dist/types/responses.js.map +1 -0
- package/dist/types/vikunja.d.ts +319 -0
- package/dist/types/vikunja.d.ts.map +1 -0
- package/dist/types/vikunja.js +7 -0
- package/dist/types/vikunja.js.map +1 -0
- package/dist/utils/auth-error-handler.d.ts +23 -0
- package/dist/utils/auth-error-handler.d.ts.map +1 -0
- package/dist/utils/auth-error-handler.js +162 -0
- package/dist/utils/auth-error-handler.js.map +1 -0
- package/dist/utils/composite-operation.d.ts +184 -0
- package/dist/utils/composite-operation.d.ts.map +1 -0
- package/dist/utils/composite-operation.js +293 -0
- package/dist/utils/composite-operation.js.map +1 -0
- package/dist/utils/error-handler.d.ts +39 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +336 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/filtering/ClientSideFilteringStrategy.d.ts +13 -0
- package/dist/utils/filtering/ClientSideFilteringStrategy.d.ts.map +1 -0
- package/dist/utils/filtering/ClientSideFilteringStrategy.js +129 -0
- package/dist/utils/filtering/ClientSideFilteringStrategy.js.map +1 -0
- package/dist/utils/filtering/FilteringContext.d.ts +40 -0
- package/dist/utils/filtering/FilteringContext.d.ts.map +1 -0
- package/dist/utils/filtering/FilteringContext.js +58 -0
- package/dist/utils/filtering/FilteringContext.js.map +1 -0
- package/dist/utils/filtering/HybridFilteringStrategy.d.ts +15 -0
- package/dist/utils/filtering/HybridFilteringStrategy.d.ts.map +1 -0
- package/dist/utils/filtering/HybridFilteringStrategy.js +55 -0
- package/dist/utils/filtering/HybridFilteringStrategy.js.map +1 -0
- package/dist/utils/filtering/RestCrossProjectFilteringStrategy.d.ts +42 -0
- package/dist/utils/filtering/RestCrossProjectFilteringStrategy.d.ts.map +1 -0
- package/dist/utils/filtering/RestCrossProjectFilteringStrategy.js +120 -0
- package/dist/utils/filtering/RestCrossProjectFilteringStrategy.js.map +1 -0
- package/dist/utils/filtering/ServerSideFilteringStrategy.d.ts +13 -0
- package/dist/utils/filtering/ServerSideFilteringStrategy.d.ts.map +1 -0
- package/dist/utils/filtering/ServerSideFilteringStrategy.js +88 -0
- package/dist/utils/filtering/ServerSideFilteringStrategy.js.map +1 -0
- package/dist/utils/filtering/TaskFilteringStrategy.d.ts +19 -0
- package/dist/utils/filtering/TaskFilteringStrategy.d.ts.map +1 -0
- package/dist/utils/filtering/TaskFilteringStrategy.js +10 -0
- package/dist/utils/filtering/TaskFilteringStrategy.js.map +1 -0
- package/dist/utils/filtering/index.d.ts +14 -0
- package/dist/utils/filtering/index.d.ts.map +1 -0
- package/dist/utils/filtering/index.js +22 -0
- package/dist/utils/filtering/index.js.map +1 -0
- package/dist/utils/filtering/types.d.ts +108 -0
- package/dist/utils/filtering/types.d.ts.map +1 -0
- package/dist/utils/filtering/types.js +6 -0
- package/dist/utils/filtering/types.js.map +1 -0
- package/dist/utils/filters.d.ts +70 -0
- package/dist/utils/filters.d.ts.map +1 -0
- package/dist/utils/filters.js +812 -0
- package/dist/utils/filters.js.map +1 -0
- package/dist/utils/http-error-detail.d.ts +28 -0
- package/dist/utils/http-error-detail.d.ts.map +1 -0
- package/dist/utils/http-error-detail.js +67 -0
- package/dist/utils/http-error-detail.js.map +1 -0
- package/dist/utils/label-bulk.d.ts +21 -0
- package/dist/utils/label-bulk.d.ts.map +1 -0
- package/dist/utils/label-bulk.js +51 -0
- package/dist/utils/label-bulk.js.map +1 -0
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +66 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/memory.d.ts +73 -0
- package/dist/utils/memory.d.ts.map +1 -0
- package/dist/utils/memory.js +194 -0
- package/dist/utils/memory.js.map +1 -0
- package/dist/utils/performance/batch-processor.d.ts +77 -0
- package/dist/utils/performance/batch-processor.d.ts.map +1 -0
- package/dist/utils/performance/batch-processor.js +218 -0
- package/dist/utils/performance/batch-processor.js.map +1 -0
- package/dist/utils/performance/index.d.ts +42 -0
- package/dist/utils/performance/index.d.ts.map +1 -0
- package/dist/utils/performance/index.js +49 -0
- package/dist/utils/performance/index.js.map +1 -0
- package/dist/utils/performance/performance-monitor.d.ts +116 -0
- package/dist/utils/performance/performance-monitor.d.ts.map +1 -0
- package/dist/utils/performance/performance-monitor.js +307 -0
- package/dist/utils/performance/performance-monitor.js.map +1 -0
- package/dist/utils/read-only.d.ts +111 -0
- package/dist/utils/read-only.d.ts.map +1 -0
- package/dist/utils/read-only.js +503 -0
- package/dist/utils/read-only.js.map +1 -0
- package/dist/utils/response-factory.d.ts +81 -0
- package/dist/utils/response-factory.d.ts.map +1 -0
- package/dist/utils/response-factory.js +86 -0
- package/dist/utils/response-factory.js.map +1 -0
- package/dist/utils/retry.d.ts +160 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +316 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/security.d.ts +70 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +358 -0
- package/dist/utils/security.js.map +1 -0
- package/dist/utils/simple-response.d.ts +75 -0
- package/dist/utils/simple-response.d.ts.map +1 -0
- package/dist/utils/simple-response.js +311 -0
- package/dist/utils/simple-response.js.map +1 -0
- package/dist/utils/storage-errors.d.ts +9 -0
- package/dist/utils/storage-errors.d.ts.map +1 -0
- package/dist/utils/storage-errors.js +20 -0
- package/dist/utils/storage-errors.js.map +1 -0
- package/dist/utils/task-rest-transport.d.ts +28 -0
- package/dist/utils/task-rest-transport.d.ts.map +1 -0
- package/dist/utils/task-rest-transport.js +33 -0
- package/dist/utils/task-rest-transport.js.map +1 -0
- package/dist/utils/unicode-fix.d.ts +19 -0
- package/dist/utils/unicode-fix.d.ts.map +1 -0
- package/dist/utils/unicode-fix.js +70 -0
- package/dist/utils/unicode-fix.js.map +1 -0
- package/dist/utils/validation.d.ts +75 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +758 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/vikunja-rest.d.ts +159 -0
- package/dist/utils/vikunja-rest.d.ts.map +1 -0
- package/dist/utils/vikunja-rest.js +378 -0
- package/dist/utils/vikunja-rest.js.map +1 -0
- package/docs/CONFIGURATION.md +957 -0
- package/docs/DOCKER-DESKTOP-MCP.md +207 -0
- package/docs/TOOLS.md +422 -0
- package/package.json +145 -0
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Zod-based filter validation and parsing system
|
|
4
|
+
* Replaces the complex custom tokenizer/parser with secure Zod schemas
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.FilterBuilder = exports.SecurityValidator = void 0;
|
|
8
|
+
exports.parseFilterString = parseFilterString;
|
|
9
|
+
exports.validateCondition = validateCondition;
|
|
10
|
+
exports.validateFilterExpression = validateFilterExpression;
|
|
11
|
+
exports.conditionToString = conditionToString;
|
|
12
|
+
exports.groupToString = groupToString;
|
|
13
|
+
exports.expressionToString = expressionToString;
|
|
14
|
+
const zod_1 = require("zod");
|
|
15
|
+
/**
|
|
16
|
+
* Security constants
|
|
17
|
+
*/
|
|
18
|
+
const MAX_FILTER_LENGTH = 1000;
|
|
19
|
+
const MAX_VALUE_LENGTH = 200;
|
|
20
|
+
const ALLOWED_CHARS = /^[\t\n\r\u0020-\u007D\u00C0-\u017F\u4E00-\u9FFF]*$/;
|
|
21
|
+
/**
|
|
22
|
+
* Pre-compiled optimized regex patterns for performance and security
|
|
23
|
+
* Using atomic groups, possessive quantifiers, and non-backtracking patterns to prevent ReDoS
|
|
24
|
+
*/
|
|
25
|
+
const DATE_PATTERNS = {
|
|
26
|
+
// Combined pattern with atomic groups to prevent backtracking
|
|
27
|
+
QUICK_DATE_CHECK: /^(?:(?:\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2})?)|now(?:[+-]\d{1,4}[smhdwMy])?|now\/[smhdwMy])$/,
|
|
28
|
+
// Individual optimized patterns for specific validation
|
|
29
|
+
ISO_DATE: /^\d{4}-\d{2}-\d{2}$/,
|
|
30
|
+
ISO_DATETIME: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/,
|
|
31
|
+
NOW_LITERAL: /^now$/,
|
|
32
|
+
RELATIVE_DATE: /^now([+-]\d{1,4}[smhdwMy])$/,
|
|
33
|
+
PERIOD_DATE: /^now\/([smhdwMy])$/,
|
|
34
|
+
// Fast rejection patterns - optimized for performance
|
|
35
|
+
SECURITY_REJECTION: [
|
|
36
|
+
/\s/, // Any spaces
|
|
37
|
+
/now\+\d+day/, // "day" instead of "d"
|
|
38
|
+
/now\/day/, // "day" instead of "d"
|
|
39
|
+
/\d{4}\/\d{1}\/\d{1}/, // Missing leading zeros in YYYY/M/D
|
|
40
|
+
/\d{1}-\d{2}-\d{4}/, // Wrong order D-MM-YYYY
|
|
41
|
+
/now\+\d+\.\d+[a-z]/, // Decimal numbers
|
|
42
|
+
/now\+\+/, // Double operator
|
|
43
|
+
/now\+-/, // Conflicting operators
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
// Repeated character check for DoS prevention - optimized pattern
|
|
47
|
+
const REPEATED_CHAR_PATTERN = /(.)\1{20,}/;
|
|
48
|
+
/**
|
|
49
|
+
* Zod schemas for validation
|
|
50
|
+
*/
|
|
51
|
+
const FilterFieldSchema = zod_1.z.enum([
|
|
52
|
+
'done', 'priority', 'percentDone', 'dueDate', 'startDate', 'endDate',
|
|
53
|
+
'doneAt', 'project', 'assignees',
|
|
54
|
+
'labels', 'created', 'updated', 'title', 'description'
|
|
55
|
+
]);
|
|
56
|
+
const FilterOperatorSchema = zod_1.z.enum([
|
|
57
|
+
'=', '!=', '>', '>=', '<', '<=', 'like', 'LIKE', 'in', 'not in'
|
|
58
|
+
]);
|
|
59
|
+
const LogicalOperatorSchema = zod_1.z.enum(['&&', '||']);
|
|
60
|
+
const FilterValueSchema = zod_1.z.union([
|
|
61
|
+
zod_1.z.string(),
|
|
62
|
+
zod_1.z.number(),
|
|
63
|
+
zod_1.z.boolean(),
|
|
64
|
+
zod_1.z.array(zod_1.z.string()),
|
|
65
|
+
zod_1.z.array(zod_1.z.number())
|
|
66
|
+
]);
|
|
67
|
+
const FilterConditionSchema = zod_1.z.object({
|
|
68
|
+
field: FilterFieldSchema,
|
|
69
|
+
operator: FilterOperatorSchema,
|
|
70
|
+
value: FilterValueSchema,
|
|
71
|
+
}).strict();
|
|
72
|
+
const FilterGroupSchema = zod_1.z.object({
|
|
73
|
+
conditions: zod_1.z.array(FilterConditionSchema).min(1, 'Group must contain at least one condition'),
|
|
74
|
+
operator: LogicalOperatorSchema.default('&&'),
|
|
75
|
+
}).strict();
|
|
76
|
+
const FilterExpressionSchema = zod_1.z.object({
|
|
77
|
+
groups: zod_1.z.array(FilterGroupSchema).min(1, 'Expression must contain at least one group'),
|
|
78
|
+
operator: LogicalOperatorSchema.optional(),
|
|
79
|
+
}).strict();
|
|
80
|
+
/**
|
|
81
|
+
* Security validation functions
|
|
82
|
+
*/
|
|
83
|
+
exports.SecurityValidator = {
|
|
84
|
+
/**
|
|
85
|
+
* Validates input string contains only allowed characters
|
|
86
|
+
*/
|
|
87
|
+
validateAllowedChars(input) {
|
|
88
|
+
return ALLOWED_CHARS.test(input);
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* Validates filter string length
|
|
92
|
+
*/
|
|
93
|
+
validateLength(input) {
|
|
94
|
+
if (input.length > MAX_FILTER_LENGTH) {
|
|
95
|
+
return {
|
|
96
|
+
isValid: false,
|
|
97
|
+
error: `Filter string too long. Maximum length is ${MAX_FILTER_LENGTH} characters, got ${input.length}`
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return { isValid: true };
|
|
101
|
+
},
|
|
102
|
+
/**
|
|
103
|
+
* Validates individual value length and safety
|
|
104
|
+
*/
|
|
105
|
+
validateValue(value) {
|
|
106
|
+
if (value.length > MAX_VALUE_LENGTH) {
|
|
107
|
+
return {
|
|
108
|
+
isValid: false,
|
|
109
|
+
error: `Value too long. Maximum length is ${MAX_VALUE_LENGTH} characters`
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return { isValid: true };
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Create parse error with context
|
|
117
|
+
*/
|
|
118
|
+
function createParseError(message, state, contextLength = 20) {
|
|
119
|
+
const start = Math.max(0, state.position - contextLength);
|
|
120
|
+
const end = Math.min(state.length, state.position + contextLength);
|
|
121
|
+
const prefix = start > 0 ? '...' : '';
|
|
122
|
+
const suffix = end < state.length ? '...' : '';
|
|
123
|
+
const context = state.input.substring(start, end);
|
|
124
|
+
const markerPosition = state.position - start + prefix.length;
|
|
125
|
+
const marker = ' '.repeat(markerPosition) + '^';
|
|
126
|
+
return {
|
|
127
|
+
message,
|
|
128
|
+
position: state.position,
|
|
129
|
+
context: `${prefix}${context}${suffix}\n${marker}`
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Skip whitespace in input
|
|
134
|
+
*/
|
|
135
|
+
function skipWhitespace(state) {
|
|
136
|
+
while (state.position < state.length && state.input[state.position] !== undefined) {
|
|
137
|
+
const char = state.input[state.position];
|
|
138
|
+
if (char && /\s/.test(char)) {
|
|
139
|
+
state.position++;
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Parse quoted string value
|
|
148
|
+
*/
|
|
149
|
+
function parseQuotedString(state) {
|
|
150
|
+
if (state.position >= state.length || state.input[state.position] !== '"') {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
state.position++; // Skip opening quote
|
|
154
|
+
let value = '';
|
|
155
|
+
while (state.position < state.length && state.input[state.position] !== '"') {
|
|
156
|
+
const char = state.input[state.position];
|
|
157
|
+
// Handle escaped quotes
|
|
158
|
+
if (char === '\\' && state.position + 1 < state.length && state.input[state.position + 1] === '"') {
|
|
159
|
+
value += '"';
|
|
160
|
+
state.position += 2;
|
|
161
|
+
}
|
|
162
|
+
else if (char !== undefined) {
|
|
163
|
+
value += char;
|
|
164
|
+
state.position++;
|
|
165
|
+
}
|
|
166
|
+
// Prevent extremely long quoted values
|
|
167
|
+
if (value.length > MAX_VALUE_LENGTH) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (state.position >= state.length) {
|
|
172
|
+
return null; // Unclosed quote
|
|
173
|
+
}
|
|
174
|
+
state.position++; // Skip closing quote
|
|
175
|
+
return value;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Parse unquoted value
|
|
179
|
+
*/
|
|
180
|
+
function parseUnquotedValue(state) {
|
|
181
|
+
const start = state.position;
|
|
182
|
+
while (state.position < state.length &&
|
|
183
|
+
state.input[state.position] !== undefined) {
|
|
184
|
+
const char = state.input[state.position];
|
|
185
|
+
if (char && /[^\s(),=!<>&|]/.test(char)) {
|
|
186
|
+
state.position++;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (start === state.position) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return state.input.substring(start, state.position);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Parse a value (quoted or unquoted)
|
|
199
|
+
*/
|
|
200
|
+
function parseValue(state) {
|
|
201
|
+
const quoted = parseQuotedString(state);
|
|
202
|
+
if (quoted !== null) {
|
|
203
|
+
return quoted;
|
|
204
|
+
}
|
|
205
|
+
return parseUnquotedValue(state);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Parse operator token
|
|
209
|
+
*/
|
|
210
|
+
function parseOperator(state) {
|
|
211
|
+
const operators = ['>=', '<=', '!=', '>', '<', '=', 'like', 'in', 'not in'];
|
|
212
|
+
for (const op of operators.sort((a, b) => b.length - a.length)) {
|
|
213
|
+
const substr = state.input.substring(state.position, state.position + op.length);
|
|
214
|
+
if (substr.toLowerCase() === op.toLowerCase()) {
|
|
215
|
+
state.position += op.length;
|
|
216
|
+
// Preserve original case
|
|
217
|
+
return substr;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Parse field name
|
|
224
|
+
*/
|
|
225
|
+
function parseField(state) {
|
|
226
|
+
// Longest-first so 'doneAt' is matched before 'done' and 'startDate'/'endDate'
|
|
227
|
+
// before any shorter prefix. parseField does a substring match with a
|
|
228
|
+
// word-boundary check; without ordering 'done' would shadow 'doneAt'.
|
|
229
|
+
const fields = ['percentDone', 'startDate', 'endDate', 'doneAt', 'dueDate',
|
|
230
|
+
'priority', 'assignees', 'project', 'labels', 'created', 'updated',
|
|
231
|
+
'title', 'description', 'done'];
|
|
232
|
+
for (const field of fields) {
|
|
233
|
+
const substr = state.input.substring(state.position, state.position + field.length);
|
|
234
|
+
if (substr === field &&
|
|
235
|
+
(state.position + field.length >= state.length ||
|
|
236
|
+
/[\s=!<>]/.test(state.input[state.position + field.length] || ''))) {
|
|
237
|
+
state.position += field.length;
|
|
238
|
+
return field;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Parse logical operator
|
|
245
|
+
*/
|
|
246
|
+
function parseLogicalOperator(state) {
|
|
247
|
+
if (state.input.substring(state.position, state.position + 2) === '&&') {
|
|
248
|
+
state.position += 2;
|
|
249
|
+
return '&&';
|
|
250
|
+
}
|
|
251
|
+
if (state.input.substring(state.position, state.position + 2) === '||') {
|
|
252
|
+
state.position += 2;
|
|
253
|
+
return '||';
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Parse comma-separated values for IN/NOT IN operators
|
|
259
|
+
*/
|
|
260
|
+
function parseArrayValues(state) {
|
|
261
|
+
const values = [];
|
|
262
|
+
const firstValue = parseValue(state);
|
|
263
|
+
if (firstValue === null) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
values.push(firstValue);
|
|
267
|
+
while (state.position < state.length) {
|
|
268
|
+
skipWhitespace(state);
|
|
269
|
+
if (state.position >= state.length || state.input[state.position] !== ',') {
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
state.position++; // Skip comma
|
|
273
|
+
skipWhitespace(state);
|
|
274
|
+
const nextValue = parseValue(state);
|
|
275
|
+
if (nextValue === null) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
values.push(nextValue);
|
|
279
|
+
}
|
|
280
|
+
return values;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Convert string value to appropriate type based on field
|
|
284
|
+
*/
|
|
285
|
+
function convertValue(value, field, operator) {
|
|
286
|
+
if (operator === 'in' || operator === 'not in') {
|
|
287
|
+
return value.split(',').map(v => v.trim());
|
|
288
|
+
}
|
|
289
|
+
const fieldType = {
|
|
290
|
+
done: 'boolean',
|
|
291
|
+
priority: 'number',
|
|
292
|
+
percentDone: 'number',
|
|
293
|
+
dueDate: 'date',
|
|
294
|
+
startDate: 'date',
|
|
295
|
+
endDate: 'date',
|
|
296
|
+
doneAt: 'date',
|
|
297
|
+
project: 'number',
|
|
298
|
+
assignees: 'array',
|
|
299
|
+
labels: 'array',
|
|
300
|
+
created: 'date',
|
|
301
|
+
updated: 'date',
|
|
302
|
+
title: 'string',
|
|
303
|
+
description: 'string',
|
|
304
|
+
}[field];
|
|
305
|
+
if (fieldType === 'boolean') {
|
|
306
|
+
return value === 'true';
|
|
307
|
+
}
|
|
308
|
+
else if (fieldType === 'number') {
|
|
309
|
+
const num = Number(value);
|
|
310
|
+
if (isNaN(num)) {
|
|
311
|
+
throw new Error(`Invalid number: ${value}`);
|
|
312
|
+
}
|
|
313
|
+
return num;
|
|
314
|
+
}
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Parse a single condition
|
|
319
|
+
*/
|
|
320
|
+
function parseCondition(state) {
|
|
321
|
+
const field = parseField(state);
|
|
322
|
+
if (field === null) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
skipWhitespace(state);
|
|
326
|
+
const operator = parseOperator(state);
|
|
327
|
+
if (operator === null) {
|
|
328
|
+
throw new Error('Expected operator');
|
|
329
|
+
}
|
|
330
|
+
skipWhitespace(state);
|
|
331
|
+
let rawValue;
|
|
332
|
+
if (operator === 'in' || operator === 'not in') {
|
|
333
|
+
const values = parseArrayValues(state);
|
|
334
|
+
if (values === null) {
|
|
335
|
+
throw new Error('Expected value(s) for IN/NOT IN operator');
|
|
336
|
+
}
|
|
337
|
+
rawValue = values.join(',');
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
const value = parseValue(state);
|
|
341
|
+
if (value === null) {
|
|
342
|
+
throw new Error('Expected value');
|
|
343
|
+
}
|
|
344
|
+
rawValue = value;
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
const convertedValue = convertValue(rawValue, field, operator);
|
|
348
|
+
return { field, operator, value: convertedValue };
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
throw new Error(error instanceof Error ? error.message : 'Invalid value');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Parse a group (conditions optionally in parentheses)
|
|
356
|
+
*/
|
|
357
|
+
function parseGroup(state) {
|
|
358
|
+
const conditions = [];
|
|
359
|
+
let operator = '&&';
|
|
360
|
+
let hasParens = false;
|
|
361
|
+
skipWhitespace(state);
|
|
362
|
+
if (state.position < state.length && state.input[state.position] === '(') {
|
|
363
|
+
hasParens = true;
|
|
364
|
+
state.position++; // Skip opening parenthesis
|
|
365
|
+
skipWhitespace(state);
|
|
366
|
+
}
|
|
367
|
+
// Parse first condition
|
|
368
|
+
const firstCondition = parseCondition(state);
|
|
369
|
+
if (firstCondition === null) {
|
|
370
|
+
throw new Error('Expected condition');
|
|
371
|
+
}
|
|
372
|
+
conditions.push(firstCondition);
|
|
373
|
+
skipWhitespace(state);
|
|
374
|
+
// Parse additional conditions with logical operators
|
|
375
|
+
while (state.position < state.length) {
|
|
376
|
+
// Check for closing parenthesis
|
|
377
|
+
if (hasParens && state.position < state.length && state.input[state.position] === ')') {
|
|
378
|
+
state.position++;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
// Check for logical operator
|
|
382
|
+
const logicalOp = parseLogicalOperator(state);
|
|
383
|
+
if (logicalOp === null) {
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
operator = logicalOp;
|
|
387
|
+
skipWhitespace(state);
|
|
388
|
+
// Parse next condition
|
|
389
|
+
const nextCondition = parseCondition(state);
|
|
390
|
+
if (nextCondition === null) {
|
|
391
|
+
throw new Error('Expected condition after logical operator');
|
|
392
|
+
}
|
|
393
|
+
conditions.push(nextCondition);
|
|
394
|
+
skipWhitespace(state);
|
|
395
|
+
}
|
|
396
|
+
// If we had parentheses but didn't find closing one, it's an error
|
|
397
|
+
if (hasParens && state.position <= state.length && state.input[state.position - 1] !== ')') {
|
|
398
|
+
throw new Error('Expected closing parenthesis');
|
|
399
|
+
}
|
|
400
|
+
return { conditions, operator };
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Parse complete filter expression
|
|
404
|
+
*/
|
|
405
|
+
function parseExpression(state) {
|
|
406
|
+
const groups = [];
|
|
407
|
+
let groupOperator;
|
|
408
|
+
// Parse first group
|
|
409
|
+
const firstGroup = parseGroup(state);
|
|
410
|
+
groups.push(firstGroup);
|
|
411
|
+
skipWhitespace(state);
|
|
412
|
+
// Parse additional groups with logical operators
|
|
413
|
+
while (state.position < state.length) {
|
|
414
|
+
const logicalOp = parseLogicalOperator(state);
|
|
415
|
+
if (logicalOp === null) {
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
if (!groupOperator) {
|
|
419
|
+
groupOperator = logicalOp;
|
|
420
|
+
}
|
|
421
|
+
skipWhitespace(state);
|
|
422
|
+
const nextGroup = parseGroup(state);
|
|
423
|
+
groups.push(nextGroup);
|
|
424
|
+
skipWhitespace(state);
|
|
425
|
+
}
|
|
426
|
+
const expression = groupOperator
|
|
427
|
+
? { groups, operator: groupOperator }
|
|
428
|
+
: { groups };
|
|
429
|
+
return expression;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Main filter string parsing function
|
|
433
|
+
* Replaces the complex tokenizer/parser system with Zod validation
|
|
434
|
+
*/
|
|
435
|
+
function parseFilterString(filterStr) {
|
|
436
|
+
// Input validation
|
|
437
|
+
if (typeof filterStr !== 'string') {
|
|
438
|
+
return {
|
|
439
|
+
expression: null,
|
|
440
|
+
error: {
|
|
441
|
+
message: 'Filter input must be a string',
|
|
442
|
+
position: 0,
|
|
443
|
+
},
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
if (!filterStr || filterStr.trim().length === 0) {
|
|
447
|
+
return {
|
|
448
|
+
expression: null,
|
|
449
|
+
error: {
|
|
450
|
+
message: 'Filter string cannot be empty',
|
|
451
|
+
position: 0,
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
// Security validation
|
|
456
|
+
if (!exports.SecurityValidator.validateAllowedChars(filterStr)) {
|
|
457
|
+
return {
|
|
458
|
+
expression: null,
|
|
459
|
+
error: {
|
|
460
|
+
message: 'Filter string contains invalid characters',
|
|
461
|
+
position: 0,
|
|
462
|
+
context: 'Only alphanumeric characters, common punctuation, and international characters are allowed'
|
|
463
|
+
},
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const lengthValidation = exports.SecurityValidator.validateLength(filterStr);
|
|
467
|
+
if (!lengthValidation.isValid) {
|
|
468
|
+
return {
|
|
469
|
+
expression: null,
|
|
470
|
+
error: {
|
|
471
|
+
message: lengthValidation.error || 'Filter string too long',
|
|
472
|
+
position: 0,
|
|
473
|
+
},
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
// Parse the filter string
|
|
477
|
+
const state = {
|
|
478
|
+
input: filterStr.trim(),
|
|
479
|
+
position: 0,
|
|
480
|
+
length: filterStr.trim().length
|
|
481
|
+
};
|
|
482
|
+
try {
|
|
483
|
+
const expression = parseExpression(state);
|
|
484
|
+
// Check if we consumed the entire input
|
|
485
|
+
skipWhitespace(state);
|
|
486
|
+
if (state.position < state.length) {
|
|
487
|
+
const remainingChar = state.input[state.position];
|
|
488
|
+
// Handle specific cases that should return "Invalid filter syntax"
|
|
489
|
+
if (remainingChar === '&' || remainingChar === '|' || remainingChar === '!' ||
|
|
490
|
+
remainingChar === '(' || remainingChar === ')') {
|
|
491
|
+
return {
|
|
492
|
+
expression: null,
|
|
493
|
+
error: {
|
|
494
|
+
message: 'Invalid filter syntax',
|
|
495
|
+
position: state.position,
|
|
496
|
+
context: state.input.substring(state.position, Math.min(state.position + 40, state.length))
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
expression: null,
|
|
502
|
+
error: createParseError(`Unexpected token: ${state.input.substring(state.position, Math.min(state.position + 20, state.length))}`, state)
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// Validate with Zod schema
|
|
506
|
+
const validationResult = FilterExpressionSchema.safeParse(expression);
|
|
507
|
+
if (!validationResult.success) {
|
|
508
|
+
return {
|
|
509
|
+
expression: null,
|
|
510
|
+
error: {
|
|
511
|
+
message: 'Invalid filter structure',
|
|
512
|
+
position: 0,
|
|
513
|
+
context: validationResult.error.errors.map(e => e.message).join(', ')
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
return { expression: validationResult.data };
|
|
518
|
+
}
|
|
519
|
+
catch (error) {
|
|
520
|
+
const message = error instanceof Error ? error.message : 'Parse error';
|
|
521
|
+
// Handle specific parsing errors that should return "Invalid filter syntax"
|
|
522
|
+
if (message.includes('Expected value') || message.includes('Unclosed quote')) {
|
|
523
|
+
return {
|
|
524
|
+
expression: null,
|
|
525
|
+
error: {
|
|
526
|
+
message: 'Invalid filter syntax',
|
|
527
|
+
position: state.position,
|
|
528
|
+
context: state.input.substring(Math.max(0, state.position - 20), Math.min(state.position + 20, state.length))
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
return {
|
|
533
|
+
expression: null,
|
|
534
|
+
error: createParseError(message, state)
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Enhanced validation with field type checking and value validation
|
|
540
|
+
*/
|
|
541
|
+
function validateFieldTypeAndValue(field, operator, value) {
|
|
542
|
+
const errors = [];
|
|
543
|
+
const FIELD_TYPE_MAP = {
|
|
544
|
+
done: 'boolean',
|
|
545
|
+
priority: 'number',
|
|
546
|
+
percentDone: 'number',
|
|
547
|
+
dueDate: 'date',
|
|
548
|
+
startDate: 'date',
|
|
549
|
+
endDate: 'date',
|
|
550
|
+
doneAt: 'date',
|
|
551
|
+
project: 'number',
|
|
552
|
+
assignees: 'array',
|
|
553
|
+
labels: 'array',
|
|
554
|
+
created: 'date',
|
|
555
|
+
updated: 'date',
|
|
556
|
+
title: 'string',
|
|
557
|
+
description: 'string',
|
|
558
|
+
};
|
|
559
|
+
const fieldType = FIELD_TYPE_MAP[field];
|
|
560
|
+
// Basic field validation
|
|
561
|
+
if (!Object.keys(FIELD_TYPE_MAP).includes(field)) {
|
|
562
|
+
return ['Invalid field name'];
|
|
563
|
+
}
|
|
564
|
+
// Operator validation for field types
|
|
565
|
+
if (fieldType === 'boolean' && !['=', '!='].includes(operator)) {
|
|
566
|
+
errors.push(`Invalid operator '${operator}' for boolean field '${field}'. Only = and != are allowed.`);
|
|
567
|
+
}
|
|
568
|
+
if (fieldType === 'array' && !['=', '!=', 'in', 'not in'].includes(operator)) {
|
|
569
|
+
errors.push(`Invalid operator '${operator}' for array field '${field}'. Only =, !=, in, and not in are allowed.`);
|
|
570
|
+
}
|
|
571
|
+
// Value type validation
|
|
572
|
+
if (fieldType === 'boolean') {
|
|
573
|
+
if (typeof value === 'string' && (value === 'true' || value === 'false')) {
|
|
574
|
+
// String boolean values are acceptable
|
|
575
|
+
}
|
|
576
|
+
else if (typeof value !== 'boolean') {
|
|
577
|
+
errors.push(`Field "${field}" requires a boolean value`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (fieldType === 'number' && (typeof value !== 'number' || isNaN(Number(value)))) {
|
|
581
|
+
errors.push(`Field "${field}" requires a numeric value`);
|
|
582
|
+
}
|
|
583
|
+
if (fieldType === 'array' && !Array.isArray(value) && typeof value !== 'string') {
|
|
584
|
+
errors.push(`Field "${field}" requires an array or comma-separated string`);
|
|
585
|
+
}
|
|
586
|
+
// Date validation (optimized for performance and security)
|
|
587
|
+
if (fieldType === 'date' && typeof value === 'string') {
|
|
588
|
+
// Security check: reject extremely long values that could cause DoS
|
|
589
|
+
if (value.length > 50) {
|
|
590
|
+
errors.push(`Field "${field}" requires a valid date value`);
|
|
591
|
+
return errors;
|
|
592
|
+
}
|
|
593
|
+
// Fast security check: prevent repeated characters that could indicate attacks
|
|
594
|
+
if (REPEATED_CHAR_PATTERN.test(value)) {
|
|
595
|
+
errors.push(`Field "${field}" requires a valid date value`);
|
|
596
|
+
return errors;
|
|
597
|
+
}
|
|
598
|
+
// Fast rejection: check against known invalid patterns first (optimized)
|
|
599
|
+
for (const pattern of DATE_PATTERNS.SECURITY_REJECTION) {
|
|
600
|
+
if (pattern.test(value)) {
|
|
601
|
+
errors.push(`Field "${field}" requires a valid date value`);
|
|
602
|
+
return errors;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
// Quick validation: use combined pattern for fast acceptance (prevents backtracking)
|
|
606
|
+
if (!DATE_PATTERNS.QUICK_DATE_CHECK.test(value)) {
|
|
607
|
+
errors.push(`Field "${field}" requires a valid date value`);
|
|
608
|
+
return errors;
|
|
609
|
+
}
|
|
610
|
+
// Specific validation: only run additional checks if needed
|
|
611
|
+
// This minimizes regex operations for common cases
|
|
612
|
+
if (DATE_PATTERNS.ISO_DATE.test(value)) {
|
|
613
|
+
// Validate actual calendar date only for ISO date format
|
|
614
|
+
const dateMatch = value.match(DATE_PATTERNS.ISO_DATE);
|
|
615
|
+
if (dateMatch) {
|
|
616
|
+
const [yearStr, monthStr, dayStr] = dateMatch[0].split('-');
|
|
617
|
+
if (!yearStr || !monthStr || !dayStr) {
|
|
618
|
+
errors.push(`Field "${field}" requires a valid date in YYYY-MM-DD format`);
|
|
619
|
+
return errors;
|
|
620
|
+
}
|
|
621
|
+
const year = parseInt(yearStr, 10);
|
|
622
|
+
const month = parseInt(monthStr, 10);
|
|
623
|
+
const day = parseInt(dayStr, 10);
|
|
624
|
+
const date = new Date(year, month - 1, day);
|
|
625
|
+
// Check if the date is valid (month and day within bounds)
|
|
626
|
+
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
|
|
627
|
+
errors.push(`Field "${field}" requires a valid date value`);
|
|
628
|
+
return errors;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
// No additional validation needed for other formats - they were validated by QUICK_DATE_CHECK
|
|
633
|
+
}
|
|
634
|
+
return errors;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Validate a filter condition using enhanced validation
|
|
638
|
+
*/
|
|
639
|
+
function validateCondition(condition) {
|
|
640
|
+
// Check if condition has valid structure first
|
|
641
|
+
const result = FilterConditionSchema.safeParse(condition);
|
|
642
|
+
if (!result.success) {
|
|
643
|
+
const errors = result.error.errors.map(e => e.message);
|
|
644
|
+
// Convert Zod enum error to more user-friendly message
|
|
645
|
+
if (errors.some(e => e.includes('enum value'))) {
|
|
646
|
+
return ['Invalid field name'];
|
|
647
|
+
}
|
|
648
|
+
return errors;
|
|
649
|
+
}
|
|
650
|
+
const { field, operator, value } = condition;
|
|
651
|
+
// Enhanced field and value validation
|
|
652
|
+
const fieldValidationErrors = validateFieldTypeAndValue(field, operator, value);
|
|
653
|
+
return fieldValidationErrors;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Validate filter expression using Zod
|
|
657
|
+
*/
|
|
658
|
+
function validateFilterExpression(expression, config = {}) {
|
|
659
|
+
const errors = [];
|
|
660
|
+
const warnings = [];
|
|
661
|
+
// Zod schema validation
|
|
662
|
+
const schemaResult = FilterExpressionSchema.safeParse(expression);
|
|
663
|
+
if (!schemaResult.success) {
|
|
664
|
+
errors.push(...schemaResult.error.errors.map(e => e.message));
|
|
665
|
+
}
|
|
666
|
+
// Custom validation
|
|
667
|
+
if (expression.groups) {
|
|
668
|
+
expression.groups.forEach((group, groupIndex) => {
|
|
669
|
+
if (!group.conditions || group.conditions.length === 0) {
|
|
670
|
+
errors.push(`Group ${groupIndex + 1} must contain at least one condition`);
|
|
671
|
+
}
|
|
672
|
+
group.conditions.forEach((condition, conditionIndex) => {
|
|
673
|
+
const conditionErrors = validateCondition(condition);
|
|
674
|
+
conditionErrors.forEach(errorMessage => {
|
|
675
|
+
errors.push(`Group ${groupIndex + 1}, Condition ${conditionIndex + 1}: ${errorMessage}`);
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
// Performance warnings
|
|
680
|
+
const totalConditions = expression.groups.reduce((sum, group) => sum + group.conditions.length, 0);
|
|
681
|
+
const threshold = config.performanceWarningThreshold ?? 10;
|
|
682
|
+
if (totalConditions > threshold) {
|
|
683
|
+
warnings.push(`Complex filters with many conditions (${totalConditions}) may impact performance`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const result = {
|
|
687
|
+
valid: errors.length === 0,
|
|
688
|
+
errors,
|
|
689
|
+
};
|
|
690
|
+
if (warnings.length > 0) {
|
|
691
|
+
result.warnings = warnings;
|
|
692
|
+
}
|
|
693
|
+
return result;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Maps the filter DSL's camelCase field names to the Vikunja API's snake_case
|
|
697
|
+
* Task JSON field names, for fields where they differ. Mirrors the mapping
|
|
698
|
+
* `evaluators.ts` uses for client-side evaluation (see the `evaluateCondition`
|
|
699
|
+
* switch in `src/tools/tasks/filtering/evaluators.ts`). Fields not listed here
|
|
700
|
+
* are identical between the DSL and the API (e.g. `done`, `priority`,
|
|
701
|
+
* `assignees`, `labels`, `created`, `updated`, `title`, `description`).
|
|
702
|
+
*
|
|
703
|
+
* Without this translation, the server-side `filter` query string built by
|
|
704
|
+
* `conditionToString`/`expressionToString` sends DSL field names verbatim
|
|
705
|
+
* (e.g. `dueDate`), which the API does not recognize as Task fields
|
|
706
|
+
* (it expects `due_date`).
|
|
707
|
+
*/
|
|
708
|
+
const FILTER_FIELD_TO_API_FIELD = {
|
|
709
|
+
percentDone: 'percent_done',
|
|
710
|
+
dueDate: 'due_date',
|
|
711
|
+
startDate: 'start_date',
|
|
712
|
+
endDate: 'end_date',
|
|
713
|
+
doneAt: 'done_at',
|
|
714
|
+
project: 'project_id',
|
|
715
|
+
};
|
|
716
|
+
/**
|
|
717
|
+
* Convert condition to string representation
|
|
718
|
+
*/
|
|
719
|
+
function conditionToString(condition) {
|
|
720
|
+
const { field, operator, value } = condition;
|
|
721
|
+
const apiField = FILTER_FIELD_TO_API_FIELD[field] ?? field;
|
|
722
|
+
let valueStr;
|
|
723
|
+
if (Array.isArray(value)) {
|
|
724
|
+
valueStr = value.join(', ');
|
|
725
|
+
}
|
|
726
|
+
else if (typeof value === 'string' && operator === 'like') {
|
|
727
|
+
valueStr = `"${value}"`;
|
|
728
|
+
}
|
|
729
|
+
else if (typeof value === 'boolean') {
|
|
730
|
+
valueStr = value.toString();
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
valueStr = String(value);
|
|
734
|
+
}
|
|
735
|
+
if (operator === 'in' || operator === 'not in') {
|
|
736
|
+
return `${apiField} ${operator} ${valueStr}`;
|
|
737
|
+
}
|
|
738
|
+
return `${apiField} ${operator} ${valueStr}`;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Convert group to string representation
|
|
742
|
+
*/
|
|
743
|
+
function groupToString(group) {
|
|
744
|
+
const conditions = group.conditions.map(conditionToString);
|
|
745
|
+
return conditions.length > 1
|
|
746
|
+
? `(${conditions.join(` ${group.operator} `)})`
|
|
747
|
+
: conditions[0] || '';
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Convert expression to string representation
|
|
751
|
+
*/
|
|
752
|
+
function expressionToString(expression) {
|
|
753
|
+
const groups = expression.groups.map(groupToString);
|
|
754
|
+
const operator = expression.operator || '&&';
|
|
755
|
+
return groups.join(` ${operator} `);
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Filter builder class for fluent construction
|
|
759
|
+
*/
|
|
760
|
+
class FilterBuilder {
|
|
761
|
+
expression;
|
|
762
|
+
currentGroup;
|
|
763
|
+
constructor() {
|
|
764
|
+
this.currentGroup = {
|
|
765
|
+
conditions: [],
|
|
766
|
+
operator: '&&',
|
|
767
|
+
};
|
|
768
|
+
this.expression = {
|
|
769
|
+
groups: [this.currentGroup],
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
where(field, operator, value) {
|
|
773
|
+
this.currentGroup.conditions.push({
|
|
774
|
+
field,
|
|
775
|
+
operator,
|
|
776
|
+
value: value,
|
|
777
|
+
});
|
|
778
|
+
return this;
|
|
779
|
+
}
|
|
780
|
+
and() {
|
|
781
|
+
this.currentGroup.operator = '&&';
|
|
782
|
+
return this;
|
|
783
|
+
}
|
|
784
|
+
or() {
|
|
785
|
+
this.currentGroup.operator = '||';
|
|
786
|
+
return this;
|
|
787
|
+
}
|
|
788
|
+
group(operator = '&&') {
|
|
789
|
+
this.currentGroup = {
|
|
790
|
+
conditions: [],
|
|
791
|
+
operator,
|
|
792
|
+
};
|
|
793
|
+
this.expression.groups.push(this.currentGroup);
|
|
794
|
+
return this;
|
|
795
|
+
}
|
|
796
|
+
groupOperator(operator) {
|
|
797
|
+
this.expression.operator = operator;
|
|
798
|
+
return this;
|
|
799
|
+
}
|
|
800
|
+
build() {
|
|
801
|
+
this.expression.groups = this.expression.groups.filter((g) => g.conditions.length > 0);
|
|
802
|
+
return this.expression;
|
|
803
|
+
}
|
|
804
|
+
toString() {
|
|
805
|
+
return expressionToString(this.build());
|
|
806
|
+
}
|
|
807
|
+
validate(config) {
|
|
808
|
+
return validateFilterExpression(this.build(), config);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
exports.FilterBuilder = FilterBuilder;
|
|
812
|
+
//# sourceMappingURL=filters.js.map
|