@taskforcehq/taskforce 0.3.311 → 0.3.313

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/README.md +40 -2
  2. package/dist/TaskforceCore.js +1 -1
  3. package/dist/components/features/InitiativesModule.js +1 -1
  4. package/dist/components/features/TaskSettings.js +1 -1
  5. package/dist/components/views/StandaloneLayout.js +3 -6
  6. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +1 -2
  7. package/dist/components/views/standalone/modals/AccountHubModal.js +2 -3
  8. package/dist/components/views/standalone/modals/HelpModal.js +15 -2
  9. package/dist/hooks/useFilterSort.js +1 -1
  10. package/dist/hooks/useTaskforce.js +2 -2
  11. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +5 -2
  12. package/dist/ui/assets/{AgentsModule-D8u9P5dZ.js → AgentsModule-BULqIo8e.js} +1 -1
  13. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-CsUauFA2.js → AnnotatedAttachmentWorkspace-DHr4rHK9.js} +1 -1
  14. package/dist/ui/assets/{ContextAttachmentManager-DbQvDHFz.js → ContextAttachmentManager-DoMe2OpY.js} +1 -1
  15. package/dist/ui/assets/{DocumentWorkspace-BmTkN1A8.js → DocumentWorkspace-DumBWhvb.js} +1 -1
  16. package/dist/ui/assets/{EntityActivityTimeline-BXHTM-Nh.js → EntityActivityTimeline-sHk8Nzbq.js} +1 -1
  17. package/dist/ui/assets/{InitiativesModule-D33RyXx0.js → InitiativesModule-3X9UNzHh.js} +1 -1
  18. package/dist/ui/assets/{PlansPage-D0RXlT9_.js → PlansPage-C3A3EYaV.js} +1 -1
  19. package/dist/ui/assets/{TaskContextUpload-Vsg67lpR.js → TaskContextUpload-C8G7pWkX.js} +1 -1
  20. package/dist/ui/assets/{TaskSettings-DshLw7_d.js → TaskSettings-DM1o9Wla.js} +2 -2
  21. package/dist/ui/assets/{WorkflowsModule-CLM-1BnF.js → WorkflowsModule-CW0crzvD.js} +1 -1
  22. package/dist/ui/assets/documentReferences-BZB3nMRb.js +1 -0
  23. package/dist/ui/assets/index-BWFQ9mGn.js +5 -0
  24. package/dist/ui/assets/{index-DOR9PRqt.css → index-DcK_8tuE.css} +1 -1
  25. package/dist/ui/index.html +2 -2
  26. package/package.json +12 -4
  27. package/scripts/postinstall.js +4 -3
  28. package/dist/TaskforceCore.CategoryFallback.test.d.ts +0 -1
  29. package/dist/TaskforceCore.CategoryFallback.test.js +0 -101
  30. package/dist/TaskforceCore.filter-persistence.test.d.ts +0 -1
  31. package/dist/TaskforceCore.filter-persistence.test.js +0 -402
  32. package/dist/TaskforceCore.routing.test.d.ts +0 -1
  33. package/dist/TaskforceCore.routing.test.js +0 -311
  34. package/dist/TaskforceCore.test.d.ts +0 -1
  35. package/dist/TaskforceCore.test.js +0 -7910
  36. package/dist/TaskforceWidget.test.d.ts +0 -1
  37. package/dist/TaskforceWidget.test.js +0 -92
  38. package/dist/UnsavedChangesRelaxed.test.d.ts +0 -1
  39. package/dist/UnsavedChangesRelaxed.test.js +0 -16
  40. package/dist/__tests__/localizationCatalogs.test.d.ts +0 -1
  41. package/dist/__tests__/localizationCatalogs.test.js +0 -54
  42. package/dist/__tests__/workflowSimulation.test.d.ts +0 -1
  43. package/dist/__tests__/workflowSimulation.test.js +0 -113
  44. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.d.ts +0 -1
  45. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.js +0 -278
  46. package/dist/annotatedAttachments/payload.test.d.ts +0 -1
  47. package/dist/annotatedAttachments/payload.test.js +0 -67
  48. package/dist/compat/workspaceSyncCompat.test.d.ts +0 -1
  49. package/dist/compat/workspaceSyncCompat.test.js +0 -124
  50. package/dist/components/features/AgentsModule.test.d.ts +0 -1
  51. package/dist/components/features/AgentsModule.test.js +0 -601
  52. package/dist/components/features/AnnotatedAttachmentWorkspace.test.d.ts +0 -1
  53. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +0 -2883
  54. package/dist/components/features/DocumentWorkspace.test.d.ts +0 -1
  55. package/dist/components/features/DocumentWorkspace.test.js +0 -1439
  56. package/dist/components/features/TaskSettings.test.d.ts +0 -1
  57. package/dist/components/features/TaskSettings.test.js +0 -1674
  58. package/dist/components/features/settings/TaxonomyLibraryManager.test.d.ts +0 -1
  59. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +0 -141
  60. package/dist/components/task/TaskActionHeader.test.d.ts +0 -1
  61. package/dist/components/task/TaskActionHeader.test.js +0 -91
  62. package/dist/components/task/TaskCard.test.d.ts +0 -1
  63. package/dist/components/task/TaskCard.test.js +0 -513
  64. package/dist/components/task/TaskContextUpload.test.d.ts +0 -1
  65. package/dist/components/task/TaskContextUpload.test.js +0 -506
  66. package/dist/components/task/TaskForm.test.d.ts +0 -1
  67. package/dist/components/task/TaskForm.test.js +0 -946
  68. package/dist/components/task/TaskKanban.empty-column-drop.test.d.ts +0 -1
  69. package/dist/components/task/TaskKanban.empty-column-drop.test.js +0 -66
  70. package/dist/components/task/TaskKanban.planning-drop.test.d.ts +0 -1
  71. package/dist/components/task/TaskKanban.planning-drop.test.js +0 -93
  72. package/dist/components/task/TaskKanban.test.d.ts +0 -1
  73. package/dist/components/task/TaskKanban.test.js +0 -99
  74. package/dist/components/task/TaskList.test.d.ts +0 -1
  75. package/dist/components/task/TaskList.test.js +0 -473
  76. package/dist/components/task/TaskStatusActions.test.d.ts +0 -1
  77. package/dist/components/task/TaskStatusActions.test.js +0 -54
  78. package/dist/components/task/taskKanbanDropRules.test.d.ts +0 -1
  79. package/dist/components/task/taskKanbanDropRules.test.js +0 -114
  80. package/dist/components/ui/LevelSelector.test.d.ts +0 -1
  81. package/dist/components/ui/LevelSelector.test.js +0 -55
  82. package/dist/components/ui/Markdown.test.d.ts +0 -1
  83. package/dist/components/ui/Markdown.test.js +0 -38
  84. package/dist/components/ui/MultiSelectFilter.test.d.ts +0 -1
  85. package/dist/components/ui/MultiSelectFilter.test.js +0 -74
  86. package/dist/components/ui/TaxonomyDropdown.test.d.ts +0 -1
  87. package/dist/components/ui/TaxonomyDropdown.test.js +0 -130
  88. package/dist/components/ui/TopNoticeLayer.test.d.ts +0 -1
  89. package/dist/components/ui/TopNoticeLayer.test.js +0 -25
  90. package/dist/components/views/AppShellHeader.test.d.ts +0 -1
  91. package/dist/components/views/AppShellHeader.test.js +0 -42
  92. package/dist/components/views/PlanComparisonPage.test.d.ts +0 -1
  93. package/dist/components/views/PlanComparisonPage.test.js +0 -758
  94. package/dist/components/views/PlansPage.test.d.ts +0 -1
  95. package/dist/components/views/PlansPage.test.js +0 -710
  96. package/dist/components/views/chrome/WorkspaceChromeSections.test.d.ts +0 -1
  97. package/dist/components/views/chrome/WorkspaceChromeSections.test.js +0 -54
  98. package/dist/components/views/chrome/workspaceChrome.test.d.ts +0 -1
  99. package/dist/components/views/chrome/workspaceChrome.test.js +0 -52
  100. package/dist/components/views/panels/DocumentFilterToolbar.test.d.ts +0 -1
  101. package/dist/components/views/panels/DocumentFilterToolbar.test.js +0 -19
  102. package/dist/components/views/panels/FilterToolbar.test.d.ts +0 -1
  103. package/dist/components/views/panels/FilterToolbar.test.js +0 -47
  104. package/dist/components/views/panels/PlanningDrawer.test.d.ts +0 -1
  105. package/dist/components/views/panels/PlanningDrawer.test.js +0 -529
  106. package/dist/components/views/planningScope.test.d.ts +0 -1
  107. package/dist/components/views/planningScope.test.js +0 -60
  108. package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +0 -1
  109. package/dist/components/views/standalone/modals/AccountHubModal.test.js +0 -72
  110. package/dist/components/views/standalone/modals/HelpModal.test.d.ts +0 -1
  111. package/dist/components/views/standalone/modals/HelpModal.test.js +0 -23
  112. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +0 -1
  113. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +0 -24
  114. package/dist/components/views/teamManagementAccess.test.d.ts +0 -1
  115. package/dist/components/views/teamManagementAccess.test.js +0 -44
  116. package/dist/config/deployment.test.d.ts +0 -1
  117. package/dist/config/deployment.test.js +0 -90
  118. package/dist/config/envSchema.test.d.ts +0 -1
  119. package/dist/config/envSchema.test.js +0 -125
  120. package/dist/core/AiProfileService.test.d.ts +0 -1
  121. package/dist/core/AiProfileService.test.js +0 -184
  122. package/dist/core/AiProfiles.test.d.ts +0 -1
  123. package/dist/core/AiProfiles.test.js +0 -1873
  124. package/dist/core/AuthTokenService.test.d.ts +0 -1
  125. package/dist/core/AuthTokenService.test.js +0 -78
  126. package/dist/core/BulkUpdateFieldsPreview.test.d.ts +0 -1
  127. package/dist/core/BulkUpdateFieldsPreview.test.js +0 -62
  128. package/dist/core/CompletedAt.test.d.ts +0 -1
  129. package/dist/core/CompletedAt.test.js +0 -44
  130. package/dist/core/CreatedBy.test.d.ts +0 -1
  131. package/dist/core/CreatedBy.test.js +0 -188
  132. package/dist/core/DeletedTaskTrash.test.d.ts +0 -1
  133. package/dist/core/DeletedTaskTrash.test.js +0 -104
  134. package/dist/core/EntitlementsPolicy.test.d.ts +0 -1
  135. package/dist/core/EntitlementsPolicy.test.js +0 -280
  136. package/dist/core/GlobalSettingsService.test.d.ts +0 -1
  137. package/dist/core/GlobalSettingsService.test.js +0 -45
  138. package/dist/core/JsonBackupRuntime.test.d.ts +0 -1
  139. package/dist/core/JsonBackupRuntime.test.js +0 -155
  140. package/dist/core/PlanFeatureCatalog.test.d.ts +0 -1
  141. package/dist/core/PlanFeatureCatalog.test.js +0 -139
  142. package/dist/core/PlanVersionPolicy.test.d.ts +0 -1
  143. package/dist/core/PlanVersionPolicy.test.js +0 -236
  144. package/dist/core/PlanningEntities.test.d.ts +0 -1
  145. package/dist/core/PlanningEntities.test.js +0 -509
  146. package/dist/core/ScheduleFields.test.d.ts +0 -1
  147. package/dist/core/ScheduleFields.test.js +0 -193
  148. package/dist/core/SetupState.test.d.ts +0 -1
  149. package/dist/core/SetupState.test.js +0 -113
  150. package/dist/core/SignupMonetizationSettings.test.d.ts +0 -1
  151. package/dist/core/SignupMonetizationSettings.test.js +0 -515
  152. package/dist/core/Stats.test.d.ts +0 -1
  153. package/dist/core/Stats.test.js +0 -36
  154. package/dist/core/SystemAdmin.test.d.ts +0 -1
  155. package/dist/core/SystemAdmin.test.js +0 -1106
  156. package/dist/core/TaskActivitySync.test.d.ts +0 -1
  157. package/dist/core/TaskActivitySync.test.js +0 -90
  158. package/dist/core/TaskAttachmentsCanonical.test.d.ts +0 -1
  159. package/dist/core/TaskAttachmentsCanonical.test.js +0 -356
  160. package/dist/core/TaskAuditTimeline.test.d.ts +0 -1
  161. package/dist/core/TaskAuditTimeline.test.js +0 -319
  162. package/dist/core/TaskTaxonomyValidation.test.d.ts +0 -1
  163. package/dist/core/TaskTaxonomyValidation.test.js +0 -440
  164. package/dist/core/Taskforce.ids.test.d.ts +0 -1
  165. package/dist/core/Taskforce.ids.test.js +0 -611
  166. package/dist/core/Taskforce.listTasksSlim.test.d.ts +0 -1
  167. package/dist/core/Taskforce.listTasksSlim.test.js +0 -91
  168. package/dist/core/Taskforce.mcpAuth.test.d.ts +0 -1
  169. package/dist/core/Taskforce.mcpAuth.test.js +0 -450
  170. package/dist/core/Taskforce.realtimeMutation.test.d.ts +0 -1
  171. package/dist/core/Taskforce.realtimeMutation.test.js +0 -131
  172. package/dist/core/UserProfileAvatarDrafts.test.d.ts +0 -1
  173. package/dist/core/UserProfileAvatarDrafts.test.js +0 -138
  174. package/dist/core/WorkspaceDelete.test.d.ts +0 -1
  175. package/dist/core/WorkspaceDelete.test.js +0 -159
  176. package/dist/core/WorkspacePermissions.test.d.ts +0 -1
  177. package/dist/core/WorkspacePermissions.test.js +0 -270
  178. package/dist/core/WorkspacePlanMode.test.d.ts +0 -1
  179. package/dist/core/WorkspacePlanMode.test.js +0 -383
  180. package/dist/core/WorkspaceSyncSettings.test.d.ts +0 -1
  181. package/dist/core/WorkspaceSyncSettings.test.js +0 -65
  182. package/dist/core/__tests__/TaskAttachmentsCanonical.test.d.ts +0 -1
  183. package/dist/core/__tests__/TaskAttachmentsCanonical.test.js +0 -212
  184. package/dist/documentReviews/DocumentReviewSessionStore.test.d.ts +0 -1
  185. package/dist/documentReviews/DocumentReviewSessionStore.test.js +0 -127
  186. package/dist/hooks/authGuardPolicy.test.d.ts +0 -1
  187. package/dist/hooks/authGuardPolicy.test.js +0 -76
  188. package/dist/hooks/bootstrapTimeouts.test.d.ts +0 -1
  189. package/dist/hooks/bootstrapTimeouts.test.js +0 -12
  190. package/dist/hooks/sync/controlPlane.test.d.ts +0 -1
  191. package/dist/hooks/sync/controlPlane.test.js +0 -121
  192. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +0 -1
  193. package/dist/hooks/sync/lifecyclePolicy.test.js +0 -124
  194. package/dist/hooks/sync/orchestratorShared.test.d.ts +0 -1
  195. package/dist/hooks/sync/orchestratorShared.test.js +0 -98
  196. package/dist/hooks/sync/pullLifecycle.test.d.ts +0 -1
  197. package/dist/hooks/sync/pullLifecycle.test.js +0 -80
  198. package/dist/hooks/sync/recovery.test.d.ts +0 -1
  199. package/dist/hooks/sync/recovery.test.js +0 -292
  200. package/dist/hooks/sync/transfers.test.d.ts +0 -1
  201. package/dist/hooks/sync/transfers.test.js +0 -928
  202. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +0 -1
  203. package/dist/hooks/sync/useRecentSyncEvents.test.js +0 -124
  204. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +0 -1
  205. package/dist/hooks/sync/useSyncStatusActions.test.js +0 -133
  206. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +0 -1
  207. package/dist/hooks/sync/useSyncStatusControls.test.js +0 -88
  208. package/dist/hooks/useFilterSort.test.d.ts +0 -1
  209. package/dist/hooks/useFilterSort.test.js +0 -419
  210. package/dist/hooks/useRealtimeSync.test.d.ts +0 -1
  211. package/dist/hooks/useRealtimeSync.test.js +0 -311
  212. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.d.ts +0 -1
  213. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +0 -54
  214. package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +0 -1
  215. package/dist/hooks/useSyncOrchestrator.context-assets.test.js +0 -228
  216. package/dist/hooks/useSyncOrchestrator.retry-closure.test.d.ts +0 -1
  217. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +0 -2027
  218. package/dist/hooks/useTaskData.test.d.ts +0 -1
  219. package/dist/hooks/useTaskData.test.js +0 -291
  220. package/dist/hooks/useTaskMutations.test.d.ts +0 -1
  221. package/dist/hooks/useTaskMutations.test.js +0 -256
  222. package/dist/hooks/useTaskforce.contract.test.d.ts +0 -1
  223. package/dist/hooks/useTaskforce.contract.test.js +0 -7
  224. package/dist/hooks/useTaskforce.priority-filters.test.d.ts +0 -1
  225. package/dist/hooks/useTaskforce.priority-filters.test.js +0 -28
  226. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +0 -1
  227. package/dist/hooks/useTaskforce.runtime-routing.test.js +0 -246
  228. package/dist/hooks/useTaskforce.sync-behavior.test.d.ts +0 -1
  229. package/dist/hooks/useTaskforce.sync-behavior.test.js +0 -3644
  230. package/dist/hooks/useWorkspaceSyncController.test.d.ts +0 -1
  231. package/dist/hooks/useWorkspaceSyncController.test.js +0 -192
  232. package/dist/hooks/workspace/useTaskforceApiRouting.test.d.ts +0 -1
  233. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +0 -77
  234. package/dist/hooks/workspace/workspaceLocalState.test.d.ts +0 -1
  235. package/dist/hooks/workspace/workspaceLocalState.test.js +0 -59
  236. package/dist/localization/localizationCatalogs.test.d.ts +0 -1
  237. package/dist/localization/localizationCatalogs.test.js +0 -37
  238. package/dist/mcp/aiProfileBootstrap.test.d.ts +0 -1
  239. package/dist/mcp/aiProfileBootstrap.test.js +0 -68
  240. package/dist/mcp/canonicalAssetHelpers.test.d.ts +0 -1
  241. package/dist/mcp/canonicalAssetHelpers.test.js +0 -67
  242. package/dist/mcp/clientIntegrations.test.d.ts +0 -1
  243. package/dist/mcp/clientIntegrations.test.js +0 -219
  244. package/dist/mcp/optionResolution.test.d.ts +0 -1
  245. package/dist/mcp/optionResolution.test.js +0 -43
  246. package/dist/mcp/runtime.test.d.ts +0 -1
  247. package/dist/mcp/runtime.test.js +0 -5211
  248. package/dist/mcp/structuredCommentValidation.test.d.ts +0 -1
  249. package/dist/mcp/structuredCommentValidation.test.js +0 -31
  250. package/dist/mcp/structuredDocValidation.test.d.ts +0 -1
  251. package/dist/mcp/structuredDocValidation.test.js +0 -35
  252. package/dist/mcp/toolRegistry.test.d.ts +0 -1
  253. package/dist/mcp/toolRegistry.test.js +0 -34
  254. package/dist/migrations/billingSchemaParity.test.d.ts +0 -1
  255. package/dist/migrations/billingSchemaParity.test.js +0 -160
  256. package/dist/migrations/taskSchemaMigrations.test.d.ts +0 -1
  257. package/dist/migrations/taskSchemaMigrations.test.js +0 -225
  258. package/dist/qaOpen.test.d.ts +0 -1
  259. package/dist/qaOpen.test.js +0 -56
  260. package/dist/releaseVersion.test.d.ts +0 -1
  261. package/dist/releaseVersion.test.js +0 -32
  262. package/dist/runtime/appGates.test.d.ts +0 -1
  263. package/dist/runtime/appGates.test.js +0 -42
  264. package/dist/server/aiProfileAvatarAssets.test.d.ts +0 -1
  265. package/dist/server/aiProfileAvatarAssets.test.js +0 -213
  266. package/dist/server/annotatedAttachmentsRoutes.test.d.ts +0 -1
  267. package/dist/server/annotatedAttachmentsRoutes.test.js +0 -532
  268. package/dist/server/auth.test.d.ts +0 -1
  269. package/dist/server/auth.test.js +0 -131
  270. package/dist/server/buildInfo.test.d.ts +0 -1
  271. package/dist/server/buildInfo.test.js +0 -29
  272. package/dist/server/cors.test.d.ts +0 -1
  273. package/dist/server/cors.test.js +0 -77
  274. package/dist/server/documentReviewRoutes.test.d.ts +0 -1
  275. package/dist/server/documentReviewRoutes.test.js +0 -190
  276. package/dist/server/email.test.d.ts +0 -1
  277. package/dist/server/email.test.js +0 -113
  278. package/dist/server/index.cookieProxy.test.d.ts +0 -1
  279. package/dist/server/index.cookieProxy.test.js +0 -67
  280. package/dist/server/index.rateLimit.test.d.ts +0 -1
  281. package/dist/server/index.rateLimit.test.js +0 -22
  282. package/dist/server/index.test.d.ts +0 -1
  283. package/dist/server/index.test.js +0 -283
  284. package/dist/server/rateLimit.test.d.ts +0 -1
  285. package/dist/server/rateLimit.test.js +0 -107
  286. package/dist/server/realtimeSync.auth.test.d.ts +0 -1
  287. package/dist/server/realtimeSync.auth.test.js +0 -62
  288. package/dist/server/realtimeSync.replay.test.d.ts +0 -1
  289. package/dist/server/realtimeSync.replay.test.js +0 -192
  290. package/dist/server/realtimeSync.workspaceIsolation.test.d.ts +0 -1
  291. package/dist/server/realtimeSync.workspaceIsolation.test.js +0 -101
  292. package/dist/server/routes/billing.test.d.ts +0 -1
  293. package/dist/server/routes/billing.test.js +0 -3515
  294. package/dist/server/routes/shared.test.d.ts +0 -1
  295. package/dist/server/routes/shared.test.js +0 -32
  296. package/dist/server/routes/sync.integration.test.d.ts +0 -1
  297. package/dist/server/routes/sync.integration.test.js +0 -2932
  298. package/dist/server/routes/syncAuxRoutes.test.d.ts +0 -1
  299. package/dist/server/routes/syncAuxRoutes.test.js +0 -158
  300. package/dist/server/routes/syncRouteGuards.test.d.ts +0 -1
  301. package/dist/server/routes/syncRouteGuards.test.js +0 -94
  302. package/dist/server/routes.test.d.ts +0 -1
  303. package/dist/server/routes.test.js +0 -15612
  304. package/dist/server/runtimeMode.test.d.ts +0 -1
  305. package/dist/server/runtimeMode.test.js +0 -26
  306. package/dist/server/securityHeaders.test.d.ts +0 -1
  307. package/dist/server/securityHeaders.test.js +0 -70
  308. package/dist/services/templateEngine.test.d.ts +0 -1
  309. package/dist/services/templateEngine.test.js +0 -150
  310. package/dist/services/templates.test.d.ts +0 -1
  311. package/dist/services/templates.test.js +0 -45
  312. package/dist/services/workflowDocumentFixtures.test.d.ts +0 -1
  313. package/dist/services/workflowDocumentFixtures.test.js +0 -78
  314. package/dist/services/workflowExportMatrix.test.d.ts +0 -1
  315. package/dist/services/workflowExportMatrix.test.js +0 -113
  316. package/dist/services/workflowExportSnapshots.test.d.ts +0 -1
  317. package/dist/services/workflowExportSnapshots.test.js +0 -30
  318. package/dist/services/workflowGeneration.test.d.ts +0 -1
  319. package/dist/services/workflowGeneration.test.js +0 -12
  320. package/dist/services/workflowSimulation.test.d.ts +0 -1
  321. package/dist/services/workflowSimulation.test.js +0 -113
  322. package/dist/services/workflowV2Policy.test.d.ts +0 -1
  323. package/dist/services/workflowV2Policy.test.js +0 -80
  324. package/dist/shared/customTaxonomyLibrary.test.d.ts +0 -1
  325. package/dist/shared/customTaxonomyLibrary.test.js +0 -20
  326. package/dist/shared/passwordPolicy.test.d.ts +0 -1
  327. package/dist/shared/passwordPolicy.test.js +0 -34
  328. package/dist/shared/runtimeContract.test.d.ts +0 -1
  329. package/dist/shared/runtimeContract.test.js +0 -23
  330. package/dist/shared/taxonomyLibrary.test.d.ts +0 -1
  331. package/dist/shared/taxonomyLibrary.test.js +0 -26
  332. package/dist/shared/taxonomyState.test.d.ts +0 -1
  333. package/dist/shared/taxonomyState.test.js +0 -71
  334. package/dist/soak/preflight.cli.d.ts +0 -1
  335. package/dist/soak/preflight.cli.js +0 -28
  336. package/dist/soak/preflight.d.ts +0 -26
  337. package/dist/soak/preflight.js +0 -113
  338. package/dist/soak/preflight.test.d.ts +0 -1
  339. package/dist/soak/preflight.test.js +0 -39
  340. package/dist/storage/objectStorageClient.test.d.ts +0 -1
  341. package/dist/storage/objectStorageClient.test.js +0 -82
  342. package/dist/storage/objectStorageConfig.test.d.ts +0 -1
  343. package/dist/storage/objectStorageConfig.test.js +0 -50
  344. package/dist/storage/postgresAdapter.test.d.ts +0 -1
  345. package/dist/storage/postgresAdapter.test.js +0 -29
  346. package/dist/storage/workspaceAssetStore.test.d.ts +0 -1
  347. package/dist/storage/workspaceAssetStore.test.js +0 -588
  348. package/dist/sync/cloudSyncApi.test.d.ts +0 -1
  349. package/dist/sync/cloudSyncApi.test.js +0 -111
  350. package/dist/sync/collaborationSyncPayload.test.d.ts +0 -1
  351. package/dist/sync/collaborationSyncPayload.test.js +0 -125
  352. package/dist/sync/contentSyncPayload.test.d.ts +0 -1
  353. package/dist/sync/contentSyncPayload.test.js +0 -143
  354. package/dist/sync/encryptionService.test.d.ts +0 -1
  355. package/dist/sync/encryptionService.test.js +0 -52
  356. package/dist/sync/planningSyncPayload.test.d.ts +0 -1
  357. package/dist/sync/planningSyncPayload.test.js +0 -92
  358. package/dist/sync/realtimeFeature.test.d.ts +0 -1
  359. package/dist/sync/realtimeFeature.test.js +0 -30
  360. package/dist/sync/realtimeReconcile.test.d.ts +0 -1
  361. package/dist/sync/realtimeReconcile.test.js +0 -43
  362. package/dist/sync/syncApplyHandlers.test.d.ts +0 -1
  363. package/dist/sync/syncApplyHandlers.test.js +0 -626
  364. package/dist/sync/syncService.test.d.ts +0 -1
  365. package/dist/sync/syncService.test.js +0 -903
  366. package/dist/sync/taskSyncPayload.test.d.ts +0 -1
  367. package/dist/sync/taskSyncPayload.test.js +0 -54
  368. package/dist/sync/workspacePullFeed.test.d.ts +0 -1
  369. package/dist/sync/workspacePullFeed.test.js +0 -438
  370. package/dist/sync/workspaceSyncModel.test.d.ts +0 -1
  371. package/dist/sync/workspaceSyncModel.test.js +0 -1442
  372. package/dist/test/setup.d.ts +0 -1
  373. package/dist/test/setup.js +0 -15
  374. package/dist/ui/assets/documentReferences-BPWp6ce0.js +0 -1
  375. package/dist/ui/assets/index-zvbo2Jse.js +0 -5
  376. package/dist/utils/accountProfileSummaryCache.test.d.ts +0 -1
  377. package/dist/utils/accountProfileSummaryCache.test.js +0 -63
  378. package/dist/utils/annotatedAttachments.test.d.ts +0 -1
  379. package/dist/utils/annotatedAttachments.test.js +0 -108
  380. package/dist/utils/assignees.test.d.ts +0 -1
  381. package/dist/utils/assignees.test.js +0 -25
  382. package/dist/utils/avatarUpload.test.d.ts +0 -1
  383. package/dist/utils/avatarUpload.test.js +0 -40
  384. package/dist/utils/canonicalAssetKind.test.d.ts +0 -1
  385. package/dist/utils/canonicalAssetKind.test.js +0 -24
  386. package/dist/utils/coalescedRefresh.test.d.ts +0 -1
  387. package/dist/utils/coalescedRefresh.test.js +0 -30
  388. package/dist/utils/constants.icons.test.d.ts +0 -1
  389. package/dist/utils/constants.icons.test.js +0 -41
  390. package/dist/utils/planParser.test.d.ts +0 -1
  391. package/dist/utils/planParser.test.js +0 -183
  392. package/dist/utils/planningReferences.test.d.ts +0 -1
  393. package/dist/utils/planningReferences.test.js +0 -17
  394. package/dist/utils/supportContact.test.d.ts +0 -1
  395. package/dist/utils/supportContact.test.js +0 -19
  396. package/dist/utils/syncEventDetails.test.d.ts +0 -1
  397. package/dist/utils/syncEventDetails.test.js +0 -23
  398. package/dist/utils/syncEventPresentation.test.d.ts +0 -1
  399. package/dist/utils/syncEventPresentation.test.js +0 -64
  400. package/dist/utils/syncStatusPresentation.test.d.ts +0 -1
  401. package/dist/utils/syncStatusPresentation.test.js +0 -164
  402. package/dist/utils/taskActivity.test.d.ts +0 -1
  403. package/dist/utils/taskActivity.test.js +0 -218
  404. package/dist/utils/taskDraftDefaults.test.d.ts +0 -1
  405. package/dist/utils/taskDraftDefaults.test.js +0 -34
  406. package/dist/utils/taskDragZones.test.d.ts +0 -1
  407. package/dist/utils/taskDragZones.test.js +0 -14
  408. package/dist/utils/taskNormalization.test.d.ts +0 -1
  409. package/dist/utils/taskNormalization.test.js +0 -158
  410. package/dist/utils/taskSearch.test.d.ts +0 -1
  411. package/dist/utils/taskSearch.test.js +0 -69
  412. package/dist/utils/taskUtils.test.d.ts +0 -1
  413. package/dist/utils/taskUtils.test.js +0 -19
  414. package/dist/utils/taskforceApiClient.test.d.ts +0 -1
  415. package/dist/utils/taskforceApiClient.test.js +0 -44
  416. package/dist/utils/taxonomyFormDefaults.test.d.ts +0 -1
  417. package/dist/utils/taxonomyFormDefaults.test.js +0 -63
  418. package/dist/utils/uiNotice.test.d.ts +0 -1
  419. package/dist/utils/uiNotice.test.js +0 -18
  420. package/dist/utils/workspaceIdentity.test.d.ts +0 -1
  421. package/dist/utils/workspaceIdentity.test.js +0 -61
  422. package/dist/utils/workspaceLifecycle.test.d.ts +0 -1
  423. package/dist/utils/workspaceLifecycle.test.js +0 -64
  424. package/dist/utils/workspaceSyncPresentation.test.d.ts +0 -1
  425. package/dist/utils/workspaceSyncPresentation.test.js +0 -71
  426. package/scripts/bootstrap-playwright-auth.mjs +0 -155
  427. package/scripts/check-cloud-mcp.mjs +0 -83
  428. package/scripts/check-compat-boundaries.mjs +0 -41
  429. package/scripts/check-env.ts +0 -31
  430. package/scripts/check-localization-strings.mjs +0 -49
  431. package/scripts/check-runtime-boundaries.mjs +0 -44
  432. package/scripts/debug-global.mjs +0 -34
  433. package/scripts/debug-login.mjs +0 -32
  434. package/scripts/debug-server.mjs +0 -36
  435. package/scripts/deploy-prod.sh +0 -41
  436. package/scripts/deploy-staging.sh +0 -23
  437. package/scripts/export-sqlite-to-postgres.mjs +0 -123
  438. package/scripts/generate-env-example.ts +0 -8
  439. package/scripts/mcp-stdio-adapter.mjs +0 -160
  440. package/scripts/migrate-task-data-assets.mjs +0 -408
  441. package/scripts/playwright-auth.sh +0 -19
  442. package/scripts/qa-open-lib.mjs +0 -175
  443. package/scripts/qa-open-lib.mjs.d.ts +0 -49
  444. package/scripts/qa-open.mjs +0 -352
  445. package/scripts/release-version-lib.mjs +0 -88
  446. package/scripts/release-version.mjs +0 -72
  447. package/scripts/run-billing-performance-smoke.sh +0 -24
  448. package/scripts/run-dev-proxy.sh +0 -51
  449. package/scripts/run-e2e-realtime.sh +0 -30
  450. package/scripts/run-smoke.sh +0 -35
  451. package/scripts/run-soak.sh +0 -41
  452. package/scripts/test-global.js +0 -143
  453. package/scripts/update-homebrew-formula.sh +0 -108
  454. package/scripts/with-esbuild.sh +0 -11
@@ -1,283 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { ensureBillingWebhookAuthExclusion, ensurePublicSiteConfigAuthExclusion, configureHttpServerTimeouts, isBillingWebhookPath, rewriteProxySetCookieForLocalHost, resolveHttpServerTimeoutSettings, resolveEffectiveRequestUserId, resolveAuthorizedWorkspaceAccess, resolveDisabledAccessError, resolveMaxPayloadBytesForPath, shouldApplyReadOnlyMutationGuard, shouldRewriteProxySetCookieDomain, shouldApplyAuthRateLimitForPath, shouldProxyCloudPath, writeJsonErrorIfPossible } from './index.js';
3
- describe('server index helper policies', () => {
4
- it('uses proxy-friendly HTTP server timeouts by default', () => {
5
- const settings = resolveHttpServerTimeoutSettings({});
6
- expect(settings.keepAliveTimeoutMs).toBe(65_000);
7
- expect(settings.headersTimeoutMs).toBe(70_000);
8
- expect(settings.requestTimeoutMs).toBe(120_000);
9
- });
10
- it('keeps headers timeout above keep-alive timeout for reverse proxies', () => {
11
- const settings = resolveHttpServerTimeoutSettings({
12
- TASKFORCE_HTTP_KEEP_ALIVE_TIMEOUT_MS: '80000',
13
- TASKFORCE_HTTP_HEADERS_TIMEOUT_MS: '1000',
14
- TASKFORCE_HTTP_REQUEST_TIMEOUT_MS: '150000'
15
- });
16
- expect(settings.keepAliveTimeoutMs).toBe(80_000);
17
- expect(settings.headersTimeoutMs).toBe(85_000);
18
- expect(settings.requestTimeoutMs).toBe(150_000);
19
- });
20
- it('applies resolved HTTP server timeouts to the Node server', () => {
21
- const server = {
22
- keepAliveTimeout: 0,
23
- headersTimeout: 0,
24
- requestTimeout: 0
25
- };
26
- const settings = configureHttpServerTimeouts(server, {
27
- TASKFORCE_HTTP_KEEP_ALIVE_TIMEOUT_MS: '61000',
28
- TASKFORCE_HTTP_HEADERS_TIMEOUT_MS: '66000',
29
- TASKFORCE_HTTP_REQUEST_TIMEOUT_MS: '90000'
30
- });
31
- expect(settings).toEqual({
32
- keepAliveTimeoutMs: 61_000,
33
- headersTimeoutMs: 66_000,
34
- requestTimeoutMs: 90_000
35
- });
36
- expect(server.keepAliveTimeout).toBe(61_000);
37
- expect(server.headersTimeout).toBe(66_000);
38
- expect(server.requestTimeout).toBe(90_000);
39
- });
40
- it('adds webhook path to auth exclusions without duplicates', () => {
41
- const base = ['/api/taskforce/health', '/api/taskforce/auth'];
42
- const next = ensureBillingWebhookAuthExclusion(base);
43
- expect(next).toContain('/api/taskforce/billing/webhook');
44
- expect(next.filter((entry) => entry === '/api/taskforce/billing/webhook')).toHaveLength(1);
45
- });
46
- it('adds public site config path to auth exclusions without duplicates', () => {
47
- const base = ['/api/taskforce/health', '/api/taskforce/auth'];
48
- const next = ensurePublicSiteConfigAuthExclusion(base);
49
- expect(next).toContain('/api/taskforce/public/site-config');
50
- expect(next.filter((entry) => entry === '/api/taskforce/public/site-config')).toHaveLength(1);
51
- expect(next).toContain('/api/taskforce/public/site-config-script');
52
- expect(next.filter((entry) => entry === '/api/taskforce/public/site-config-script')).toHaveLength(1);
53
- });
54
- it('identifies billing webhook path exactly', () => {
55
- expect(isBillingWebhookPath('/api/taskforce/billing/webhook')).toBe(true);
56
- expect(isBillingWebhookPath('/api/taskforce/billing/webhook/v2')).toBe(false);
57
- });
58
- it('applies auth limiter only to auth routes except session', () => {
59
- expect(shouldApplyAuthRateLimitForPath('/api/taskforce/auth/login')).toBe(true);
60
- expect(shouldApplyAuthRateLimitForPath('/api/taskforce/auth/session')).toBe(false);
61
- expect(shouldApplyAuthRateLimitForPath('/api/taskforce/billing/webhook')).toBe(false);
62
- });
63
- it('allows workspace sync-sized ui-state payloads through the global guard', () => {
64
- expect(resolveMaxPayloadBytesForPath('/api/taskforce/ui-state', 1_048_576)).toBe(50 * 1024 * 1024);
65
- expect(resolveMaxPayloadBytesForPath('/api/taskforce/sync/workspace/push', 1_048_576)).toBe(50 * 1024 * 1024);
66
- expect(resolveMaxPayloadBytesForPath('/api/taskforce/task', 1_048_576)).toBe(1_048_576);
67
- });
68
- it('writes a json error when headers are still available', () => {
69
- let headersSent = false;
70
- let writableEnded = false;
71
- const res = {
72
- writeHead: vi.fn(() => {
73
- headersSent = true;
74
- }),
75
- end: vi.fn(() => {
76
- writableEnded = true;
77
- })
78
- };
79
- Object.defineProperty(res, 'headersSent', {
80
- configurable: true,
81
- get: () => headersSent
82
- });
83
- Object.defineProperty(res, 'writableEnded', {
84
- configurable: true,
85
- get: () => writableEnded
86
- });
87
- expect(writeJsonErrorIfPossible(res, 502, { error: 'Cloud proxy request failed.' })).toBe(true);
88
- expect(res.writeHead).toHaveBeenCalledWith(502, { 'Content-Type': 'application/json' });
89
- expect(res.end).toHaveBeenCalledWith(JSON.stringify({ error: 'Cloud proxy request failed.' }));
90
- });
91
- it('skips json error writes after headers are already sent', () => {
92
- const res = {
93
- writeHead: vi.fn(),
94
- end: vi.fn()
95
- };
96
- Object.defineProperty(res, 'headersSent', {
97
- configurable: true,
98
- get: () => true
99
- });
100
- Object.defineProperty(res, 'writableEnded', {
101
- configurable: true,
102
- get: () => false
103
- });
104
- expect(writeJsonErrorIfPossible(res, 502, { error: 'Cloud proxy request failed.' })).toBe(false);
105
- expect(res.writeHead).not.toHaveBeenCalled();
106
- expect(res.end).not.toHaveBeenCalled();
107
- });
108
- });
109
- describe('shouldProxyCloudPath', () => {
110
- it('returns false for empty or null pathname', () => {
111
- expect(shouldProxyCloudPath('')).toBe(false);
112
- expect(shouldProxyCloudPath(null)).toBe(false);
113
- });
114
- it('does not proxy local-only sync routes', () => {
115
- expect(shouldProxyCloudPath('/api/taskforce/auth/runtime-config')).toBe(false);
116
- expect(shouldProxyCloudPath('/api/taskforce/sync/workspace/apply-local')).toBe(false);
117
- expect(shouldProxyCloudPath('/api/taskforce/sync/workspace/documents')).toBe(false);
118
- expect(shouldProxyCloudPath('/api/taskforce/sync/workspace/assets')).toBe(false);
119
- expect(shouldProxyCloudPath('/api/taskforce/sync/workspace/ai-profiles')).toBe(false);
120
- expect(shouldProxyCloudPath('/api/taskforce/sync/events')).toBe(false);
121
- });
122
- it('proxies cloud-owned sync routes', () => {
123
- expect(shouldProxyCloudPath('/api/taskforce/sync/workspace/pull')).toBe(true);
124
- expect(shouldProxyCloudPath('/api/taskforce/sync/workspace/push')).toBe(true);
125
- });
126
- it('proxies cloud-owned user avatar context paths only', () => {
127
- expect(shouldProxyCloudPath('/api/taskforce/context/users%2Fuser-1%2Fprofile%2Favatar%2Favatar.webp')).toBe(true);
128
- expect(shouldProxyCloudPath('/api/taskforce/context/users/user-1/profile/avatar/avatar.webp')).toBe(true);
129
- expect(shouldProxyCloudPath('/api/taskforce/context/workspaces%2Fworkspace-1%2Fassets%2Fimages%2Fasset.png')).toBe(false);
130
- expect(shouldProxyCloudPath('/api/taskforce/context/users%2Fuser-1%2Fexports%2Favatar.webp')).toBe(false);
131
- });
132
- it('proxies admin, billing, and auth routes', () => {
133
- expect(shouldProxyCloudPath('/api/taskforce/admin/users')).toBe(true);
134
- expect(shouldProxyCloudPath('/api/taskforce/billing/plans')).toBe(true);
135
- expect(shouldProxyCloudPath('/api/taskforce/auth/login')).toBe(true);
136
- expect(shouldProxyCloudPath('/api/taskforce/account/profile-summary')).toBe(true);
137
- expect(shouldProxyCloudPath('/api/taskforce/account/team-management-access')).toBe(true);
138
- expect(shouldProxyCloudPath('/api/taskforce/public/site-config')).toBe(true);
139
- expect(shouldProxyCloudPath('/api/taskforce/public/site-config-script')).toBe(true);
140
- });
141
- it('keeps AI profile workspace mutations local so workspace sync owns replication', () => {
142
- expect(shouldProxyCloudPath('/api/taskforce/workspace/ai-profiles/avatar')).toBe(false);
143
- expect(shouldProxyCloudPath('/api/taskforce/workspace/ai-profiles/archive')).toBe(false);
144
- expect(shouldProxyCloudPath('/api/taskforce/workspace/ai-profiles/merge')).toBe(false);
145
- });
146
- });
147
- describe('proxy cookie helpers', () => {
148
- it('rewrites mismatched cookie domains for non-loopback local hosts', () => {
149
- const cookie = 'taskforce_session=abc; Path=/; HttpOnly; SameSite=Lax; Domain=taskforcehq.ai';
150
- expect(shouldRewriteProxySetCookieDomain(cookie, 'taskforce.localtest.me:3847')).toBe(true);
151
- expect(rewriteProxySetCookieForLocalHost(cookie)).not.toContain('Domain=');
152
- });
153
- it('preserves cookie domains that already match the request host', () => {
154
- const cookie = 'taskforce_session=abc; Path=/; HttpOnly; SameSite=Lax; Domain=taskforcehq.ai';
155
- expect(shouldRewriteProxySetCookieDomain(cookie, 'app.taskforcehq.ai')).toBe(false);
156
- });
157
- });
158
- describe('resolveAuthorizedWorkspaceAccess', () => {
159
- it('prefers an explicitly requested workspace when the user is authorized for it', () => {
160
- const core = {
161
- resolveUserAccess: vi.fn((userId, workspaceId, role) => {
162
- if (workspaceId !== 'workspace-team')
163
- return null;
164
- return {
165
- userId,
166
- workspaceId,
167
- role,
168
- permissionMode: 'read-write',
169
- disabled: false,
170
- betaAccess: true
171
- };
172
- }),
173
- getSystemRole: vi.fn(() => 'user')
174
- };
175
- const next = resolveAuthorizedWorkspaceAccess(core, {
176
- authenticated: true,
177
- sessionId: 'session-1',
178
- userId: 'user-1',
179
- workspaceId: 'workspace-personal',
180
- role: 'member',
181
- systemRole: 'user',
182
- identityId: null
183
- }, 'workspace-team');
184
- expect(core.resolveUserAccess).toHaveBeenCalledWith('user-1', 'workspace-team', 'member');
185
- expect(next.workspaceId).toBe('workspace-team');
186
- expect(next.userId).toBe('user-1');
187
- });
188
- it('falls back to the session workspace when the requested workspace is not authorized', () => {
189
- const core = {
190
- resolveUserAccess: vi.fn((userId, workspaceId, role) => {
191
- if (workspaceId !== 'workspace-personal')
192
- return null;
193
- return {
194
- userId,
195
- workspaceId,
196
- role,
197
- permissionMode: 'read-write',
198
- disabled: false,
199
- betaAccess: true
200
- };
201
- }),
202
- getSystemRole: vi.fn(() => 'user')
203
- };
204
- const next = resolveAuthorizedWorkspaceAccess(core, {
205
- authenticated: true,
206
- sessionId: 'session-1',
207
- userId: 'user-1',
208
- workspaceId: 'workspace-personal',
209
- role: 'member',
210
- systemRole: 'user',
211
- identityId: null
212
- }, 'workspace-team');
213
- expect(core.resolveUserAccess).toHaveBeenNthCalledWith(1, 'user-1', 'workspace-team', 'member');
214
- expect(core.resolveUserAccess).toHaveBeenNthCalledWith(2, 'user-1', 'workspace-personal', 'member');
215
- expect(next.workspaceId).toBe('workspace-personal');
216
- });
217
- it('preserves account and workspace disabled scopes separately', () => {
218
- const core = {
219
- resolveUserAccess: vi.fn((userId, workspaceId, role) => ({
220
- userId,
221
- workspaceId,
222
- role,
223
- permissionMode: 'read-write',
224
- disabled: true,
225
- accountDisabled: false,
226
- workspaceDisabled: true,
227
- betaAccess: true
228
- })),
229
- getSystemRole: vi.fn(() => 'user')
230
- };
231
- const next = resolveAuthorizedWorkspaceAccess(core, {
232
- authenticated: true,
233
- sessionId: 'session-1',
234
- userId: 'user-1',
235
- workspaceId: 'workspace-shared',
236
- role: 'member',
237
- systemRole: 'user',
238
- identityId: null
239
- }, 'workspace-shared');
240
- expect(next.disabled).toBe(true);
241
- expect(next.accountDisabled).toBe(false);
242
- expect(next.workspaceDisabled).toBe(true);
243
- });
244
- });
245
- describe('resolveDisabledAccessError', () => {
246
- it('blocks globally disabled accounts outside auth routes', () => {
247
- expect(resolveDisabledAccessError({ disabled: true, accountDisabled: true }, '/api/taskforce/workspaces')).toBe('Forbidden: user account is disabled.');
248
- expect(resolveDisabledAccessError({ disabled: true, accountDisabled: true }, '/api/taskforce/auth/session')).toBeNull();
249
- });
250
- it('allows workspace-disabled sessions to reach recovery routes only', () => {
251
- const access = { disabled: true, accountDisabled: false, workspaceDisabled: true };
252
- expect(resolveDisabledAccessError(access, '/api/taskforce/workspaces')).toBeNull();
253
- expect(resolveDisabledAccessError(access, '/api/taskforce/session/workspace')).toBeNull();
254
- expect(resolveDisabledAccessError(access, '/api/taskforce/tasks')).toBe('Forbidden: workspace access is not active for this user.');
255
- });
256
- });
257
- describe('shouldApplyReadOnlyMutationGuard', () => {
258
- it('guards ordinary Taskforce mutations', () => {
259
- expect(shouldApplyReadOnlyMutationGuard('POST', '/api/taskforce/tasks')).toBe(true);
260
- expect(shouldApplyReadOnlyMutationGuard('PATCH', '/api/taskforce/task/task-1')).toBe(true);
261
- expect(shouldApplyReadOnlyMutationGuard('DELETE', '/api/taskforce/task/task-1')).toBe(true);
262
- });
263
- it('allows session workspace switching through route-level access validation', () => {
264
- expect(shouldApplyReadOnlyMutationGuard('POST', '/api/taskforce/session/workspace')).toBe(false);
265
- });
266
- it('does not guard non-mutations, auth routes, or health checks', () => {
267
- expect(shouldApplyReadOnlyMutationGuard('GET', '/api/taskforce/tasks')).toBe(false);
268
- expect(shouldApplyReadOnlyMutationGuard('POST', '/api/taskforce/auth/login')).toBe(false);
269
- expect(shouldApplyReadOnlyMutationGuard('POST', '/api/taskforce/health')).toBe(false);
270
- });
271
- });
272
- describe('resolveEffectiveRequestUserId', () => {
273
- it('prefers the authenticated access user when present', () => {
274
- expect(resolveEffectiveRequestUserId('user-authenticated', 'user-explicit')).toBe('user-authenticated');
275
- });
276
- it('preserves an explicit local actor header when auth access is anonymous', () => {
277
- expect(resolveEffectiveRequestUserId('anonymous', 'user-local-actor')).toBe('user-local-actor');
278
- expect(resolveEffectiveRequestUserId('', 'user-local-actor')).toBe('user-local-actor');
279
- });
280
- it('falls back to anonymous when neither source has a user id', () => {
281
- expect(resolveEffectiveRequestUserId('anonymous', '')).toBe('anonymous');
282
- });
283
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,107 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { applyPrefixedRateLimitHeaders, InMemoryRateLimiter, resolveClientIp, resolveRateLimitKey } from './rateLimit';
3
- describe('InMemoryRateLimiter', () => {
4
- it('allows requests within limit and blocks over limit', () => {
5
- const limiter = new InMemoryRateLimiter(2, 60_000);
6
- const now = 1_000;
7
- const first = limiter.check('ip:1', now);
8
- const second = limiter.check('ip:1', now + 1);
9
- const third = limiter.check('ip:1', now + 2);
10
- expect(first.allowed).toBe(true);
11
- expect(first.remaining).toBe(1);
12
- expect(second.allowed).toBe(true);
13
- expect(second.remaining).toBe(0);
14
- expect(third.allowed).toBe(false);
15
- expect(third.retryAfterSeconds).toBeGreaterThan(0);
16
- });
17
- it('resets bucket after window', () => {
18
- const limiter = new InMemoryRateLimiter(1, 1_000);
19
- const first = limiter.check('ip:2', 1_000);
20
- const blocked = limiter.check('ip:2', 1_500);
21
- const reset = limiter.check('ip:2', 2_001);
22
- expect(first.allowed).toBe(true);
23
- expect(blocked.allowed).toBe(false);
24
- expect(reset.allowed).toBe(true);
25
- expect(reset.remaining).toBe(0);
26
- });
27
- });
28
- describe('resolveClientIp', () => {
29
- it('uses x-forwarded-for when present', () => {
30
- const req = {
31
- headers: { 'x-forwarded-for': '203.0.113.5, 10.0.0.1' },
32
- socket: { remoteAddress: '127.0.0.1' }
33
- };
34
- expect(resolveClientIp(req)).toBe('203.0.113.5');
35
- });
36
- it('falls back to remoteAddress', () => {
37
- const req = {
38
- headers: {},
39
- socket: { remoteAddress: '127.0.0.2' }
40
- };
41
- expect(resolveClientIp(req)).toBe('127.0.0.2');
42
- });
43
- });
44
- describe('applyPrefixedRateLimitHeaders', () => {
45
- it('applies prefixed headers without default header names', () => {
46
- const headers = new Map();
47
- const res = {
48
- setHeader: (name, value) => headers.set(name, value)
49
- };
50
- applyPrefixedRateLimitHeaders(res, {
51
- allowed: true,
52
- limit: 10,
53
- remaining: 8,
54
- resetAt: 1_700_000_000_000,
55
- retryAfterSeconds: 30
56
- }, 'X-Auth-RateLimit');
57
- expect(headers.get('X-Auth-RateLimit-Limit')).toBe('10');
58
- expect(headers.get('X-Auth-RateLimit-Remaining')).toBe('8');
59
- expect(headers.get('X-Auth-RateLimit-Reset')).toBe(String(Math.ceil(1_700_000_000_000 / 1000)));
60
- expect(headers.has('X-RateLimit-Limit')).toBe(false);
61
- });
62
- });
63
- describe('resolveRateLimitKey', () => {
64
- it('uses authenticated user identity when available', () => {
65
- const req = {
66
- method: 'GET',
67
- headers: { 'x-forwarded-for': '203.0.113.10' },
68
- socket: { remoteAddress: '127.0.0.1' }
69
- };
70
- const key = resolveRateLimitKey({
71
- req,
72
- pathname: '/api/taskforce/tasks',
73
- authenticated: true,
74
- userId: 'user-123',
75
- sessionId: 'session-abc'
76
- });
77
- expect(key).toBe('user:user-123:GET:/api/taskforce/tasks');
78
- });
79
- it('falls back to session identity for authenticated anonymous users', () => {
80
- const req = {
81
- method: 'patch',
82
- headers: {},
83
- socket: { remoteAddress: '127.0.0.5' }
84
- };
85
- const key = resolveRateLimitKey({
86
- req,
87
- pathname: '/api/taskforce/task/1',
88
- authenticated: true,
89
- userId: 'anonymous',
90
- sessionId: 'session-123'
91
- });
92
- expect(key).toBe('session:session-123:PATCH:/api/taskforce/task/1');
93
- });
94
- it('falls back to IP identity for unauthenticated requests', () => {
95
- const req = {
96
- method: 'post',
97
- headers: { 'x-forwarded-for': '198.51.100.11, 10.0.0.1' },
98
- socket: { remoteAddress: '127.0.0.1' }
99
- };
100
- const key = resolveRateLimitKey({
101
- req,
102
- pathname: '/api/taskforce/config',
103
- authenticated: false
104
- });
105
- expect(key).toBe('ip:198.51.100.11:POST:/api/taskforce/config');
106
- });
107
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,62 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { describe, expect, it, vi } from 'vitest';
3
- import { createRealtimeSyncWsManager } from './realtimeSyncWs';
4
- import { createSessionTokenCookie, resolveAuthConfigFromEnv } from './auth';
5
- class MockWs extends EventEmitter {
6
- readyState = 1;
7
- sent = [];
8
- closeCode;
9
- closeReason;
10
- send(data) {
11
- this.sent.push(data);
12
- }
13
- close(code, reason) {
14
- this.closeCode = code;
15
- this.closeReason = reason;
16
- this.readyState = 3;
17
- }
18
- }
19
- function makeReq(cookie) {
20
- return {
21
- headers: cookie ? { cookie } : {}
22
- };
23
- }
24
- describe('server/realtimeSyncWs auth', () => {
25
- it('rejects unauthenticated websocket connections when auth is required', () => {
26
- const authConfig = resolveAuthConfigFromEnv('cloud', {
27
- TASKFORCE_AUTH_ENABLED: 'true',
28
- TASKFORCE_AUTH_REQUIRED_FOR_API: 'true',
29
- TASKFORCE_AUTH_SESSION_SECRET: 'test-secret',
30
- TASKFORCE_AUTH_SESSION_COOKIE: 'taskforce_session'
31
- });
32
- const manager = createRealtimeSyncWsManager({ resolveUserAccess: vi.fn(() => null) }, authConfig, { log: vi.fn(), warn: vi.fn() });
33
- const ws = new MockWs();
34
- manager.handleConnection(ws, makeReq());
35
- expect(ws.closeCode).toBe(1008);
36
- expect(ws.closeReason).toBe('unauthorized');
37
- });
38
- it('accepts authenticated websocket connections', () => {
39
- const authConfig = resolveAuthConfigFromEnv('cloud', {
40
- TASKFORCE_AUTH_ENABLED: 'true',
41
- TASKFORCE_AUTH_REQUIRED_FOR_API: 'true',
42
- TASKFORCE_AUTH_SESSION_SECRET: 'test-secret',
43
- TASKFORCE_AUTH_SESSION_COOKIE: 'taskforce_session'
44
- });
45
- const sessionCookie = createSessionTokenCookie('session-1', authConfig, {
46
- role: 'member',
47
- workspaceId: 'workspace-a',
48
- userId: 'user-a'
49
- });
50
- const cookieHeader = sessionCookie.split(';')[0];
51
- const manager = createRealtimeSyncWsManager({
52
- resolveUserAccess: vi.fn((userId, workspaceId) => (userId === 'user-a' && workspaceId === 'workspace-a'
53
- ? { workspaceId }
54
- : null))
55
- }, authConfig, { log: vi.fn(), warn: vi.fn() });
56
- const ws = new MockWs();
57
- manager.handleConnection(ws, makeReq(cookieHeader));
58
- expect(ws.closeCode).toBeUndefined();
59
- manager.broadcastTaskforceUpdate('workspace-a');
60
- expect(ws.sent).toContain(JSON.stringify({ type: 'taskforce:update', workspaceId: 'workspace-a' }));
61
- });
62
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,192 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { describe, expect, it, vi } from 'vitest';
3
- import { createRealtimeSyncWsManager } from './realtimeSyncWs';
4
- import { createSessionTokenCookie, resolveAuthConfigFromEnv } from './auth';
5
- class MockWs extends EventEmitter {
6
- readyState = 1;
7
- bufferedAmount = 0;
8
- sent = [];
9
- closeCode;
10
- closeReason;
11
- send(data) {
12
- this.sent.push(data);
13
- }
14
- close(code, reason) {
15
- this.closeCode = code;
16
- this.closeReason = reason;
17
- this.readyState = 3;
18
- }
19
- }
20
- function makeReq(cookie) {
21
- return {
22
- headers: cookie ? { cookie } : {}
23
- };
24
- }
25
- function parseMessages(ws) {
26
- return ws.sent.map((entry) => JSON.parse(entry));
27
- }
28
- function makeEvent(seq, workspaceId = 'workspace-a', serverEpoch = 'epoch-1') {
29
- return {
30
- eventId: `${serverEpoch}:${seq}`,
31
- workspaceId,
32
- cursor: `${serverEpoch}:${seq}`,
33
- serverEpoch,
34
- seq,
35
- entityType: 'task',
36
- changeType: 'upsert',
37
- entityIds: [`task-${seq}`],
38
- dataVersion: seq,
39
- serverTs: new Date().toISOString()
40
- };
41
- }
42
- describe('server/realtimeSyncWs replay + backpressure', () => {
43
- it('replays events after a cursor for subscribed workspace', () => {
44
- const authConfig = resolveAuthConfigFromEnv('cloud', {
45
- TASKFORCE_AUTH_ENABLED: 'true',
46
- TASKFORCE_AUTH_REQUIRED_FOR_API: 'true',
47
- TASKFORCE_AUTH_SESSION_SECRET: 'test-secret',
48
- TASKFORCE_AUTH_SESSION_COOKIE: 'taskforce_session'
49
- });
50
- const manager = createRealtimeSyncWsManager({
51
- resolveUserAccess: vi.fn((userId, workspaceId) => (userId === 'user-a' && workspaceId === 'workspace-a' ? { workspaceId } : null))
52
- }, authConfig, { log: vi.fn(), warn: vi.fn() });
53
- const ws = new MockWs();
54
- const cookie = createSessionTokenCookie('session-a', authConfig, {
55
- role: 'member',
56
- workspaceId: 'workspace-a',
57
- userId: 'user-a'
58
- }).split(';')[0];
59
- manager.handleConnection(ws, makeReq(cookie));
60
- manager.broadcastMutation(makeEvent(1));
61
- manager.broadcastMutation(makeEvent(2));
62
- manager.broadcastMutation(makeEvent(3));
63
- ws.emit('message', JSON.stringify({
64
- type: 'taskforce:replay',
65
- workspaceId: 'workspace-a',
66
- cursor: 'epoch-1:1'
67
- }));
68
- const replayMsg = parseMessages(ws).find((msg) => msg.type === 'taskforce:replay');
69
- expect(replayMsg).toBeTruthy();
70
- expect(Array.isArray(replayMsg?.events)).toBe(true);
71
- const events = (replayMsg?.events || []);
72
- expect(events.map((event) => event.seq)).toEqual([2, 3]);
73
- });
74
- it('returns deterministic replay-reset on server-epoch mismatch', () => {
75
- const authConfig = resolveAuthConfigFromEnv('cloud', {
76
- TASKFORCE_AUTH_ENABLED: 'true',
77
- TASKFORCE_AUTH_REQUIRED_FOR_API: 'true',
78
- TASKFORCE_AUTH_SESSION_SECRET: 'test-secret',
79
- TASKFORCE_AUTH_SESSION_COOKIE: 'taskforce_session'
80
- });
81
- const manager = createRealtimeSyncWsManager({
82
- resolveUserAccess: vi.fn((userId, workspaceId) => (userId === 'user-a' && workspaceId === 'workspace-a' ? { workspaceId } : null))
83
- }, authConfig, { log: vi.fn(), warn: vi.fn() });
84
- const ws = new MockWs();
85
- const cookie = createSessionTokenCookie('session-a', authConfig, {
86
- role: 'member',
87
- workspaceId: 'workspace-a',
88
- userId: 'user-a'
89
- }).split(';')[0];
90
- manager.handleConnection(ws, makeReq(cookie));
91
- manager.broadcastMutation(makeEvent(1, 'workspace-a', 'epoch-2'));
92
- ws.emit('message', JSON.stringify({
93
- type: 'taskforce:replay',
94
- workspaceId: 'workspace-a',
95
- cursor: 'epoch-1:1'
96
- }));
97
- const reset = parseMessages(ws).find((msg) => msg.type === 'taskforce:replay-reset');
98
- expect(reset).toBeTruthy();
99
- expect(reset?.reason).toBe('server-epoch-mismatch');
100
- expect(reset?.serverEpoch).toBe('epoch-2');
101
- });
102
- it('returns replay-gap when cursor has been evicted from buffer window', () => {
103
- const authConfig = resolveAuthConfigFromEnv('cloud', {
104
- TASKFORCE_AUTH_ENABLED: 'true',
105
- TASKFORCE_AUTH_REQUIRED_FOR_API: 'true',
106
- TASKFORCE_AUTH_SESSION_SECRET: 'test-secret',
107
- TASKFORCE_AUTH_SESSION_COOKIE: 'taskforce_session'
108
- });
109
- const manager = createRealtimeSyncWsManager({
110
- resolveUserAccess: vi.fn((userId, workspaceId) => (userId === 'user-a' && workspaceId === 'workspace-a' ? { workspaceId } : null))
111
- }, authConfig, { log: vi.fn(), warn: vi.fn() }, { replayMaxEvents: 2, replayTtlMs: 60_000 });
112
- const ws = new MockWs();
113
- const cookie = createSessionTokenCookie('session-a', authConfig, {
114
- role: 'member',
115
- workspaceId: 'workspace-a',
116
- userId: 'user-a'
117
- }).split(';')[0];
118
- manager.handleConnection(ws, makeReq(cookie));
119
- manager.broadcastMutation(makeEvent(1));
120
- manager.broadcastMutation(makeEvent(2));
121
- manager.broadcastMutation(makeEvent(3));
122
- ws.emit('message', JSON.stringify({
123
- type: 'taskforce:replay',
124
- workspaceId: 'workspace-a',
125
- cursor: 'epoch-1:0'
126
- }));
127
- const gap = parseMessages(ws).find((msg) => msg.type === 'taskforce:replay-gap');
128
- expect(gap).toBeTruthy();
129
- expect(gap?.reason).toBe('cursor-evicted');
130
- expect(gap?.earliestSeq).toBe(2);
131
- expect(gap?.cursorSeq).toBe(0);
132
- });
133
- it('drops slow consumers instead of blocking broadcast loop', () => {
134
- const authConfig = resolveAuthConfigFromEnv('cloud', {
135
- TASKFORCE_AUTH_ENABLED: 'true',
136
- TASKFORCE_AUTH_REQUIRED_FOR_API: 'true',
137
- TASKFORCE_AUTH_SESSION_SECRET: 'test-secret',
138
- TASKFORCE_AUTH_SESSION_COOKIE: 'taskforce_session'
139
- });
140
- const manager = createRealtimeSyncWsManager({
141
- resolveUserAccess: vi.fn((userId, workspaceId) => (userId === 'user-a' && workspaceId === 'workspace-a' ? { workspaceId } : null))
142
- }, authConfig, { log: vi.fn(), warn: vi.fn() }, { maxBufferedBytes: 10 });
143
- const ws = new MockWs();
144
- ws.bufferedAmount = 9999;
145
- const cookie = createSessionTokenCookie('session-a', authConfig, {
146
- role: 'member',
147
- workspaceId: 'workspace-a',
148
- userId: 'user-a'
149
- }).split(';')[0];
150
- manager.handleConnection(ws, makeReq(cookie));
151
- manager.broadcastMutation(makeEvent(1));
152
- expect(ws.closeCode).toBe(1013);
153
- expect(ws.closeReason).toBe('slow-consumer');
154
- });
155
- it('returns stable replay batches across repeated requests with same cursor', () => {
156
- const authConfig = resolveAuthConfigFromEnv('cloud', {
157
- TASKFORCE_AUTH_ENABLED: 'true',
158
- TASKFORCE_AUTH_REQUIRED_FOR_API: 'true',
159
- TASKFORCE_AUTH_SESSION_SECRET: 'test-secret',
160
- TASKFORCE_AUTH_SESSION_COOKIE: 'taskforce_session'
161
- });
162
- const manager = createRealtimeSyncWsManager({
163
- resolveUserAccess: vi.fn((userId, workspaceId) => (userId === 'user-a' && workspaceId === 'workspace-a' ? { workspaceId } : null))
164
- }, authConfig, { log: vi.fn(), warn: vi.fn() });
165
- const ws = new MockWs();
166
- const cookie = createSessionTokenCookie('session-a', authConfig, {
167
- role: 'member',
168
- workspaceId: 'workspace-a',
169
- userId: 'user-a'
170
- }).split(';')[0];
171
- manager.handleConnection(ws, makeReq(cookie));
172
- manager.broadcastMutation(makeEvent(1));
173
- manager.broadcastMutation(makeEvent(2));
174
- manager.broadcastMutation(makeEvent(3));
175
- ws.emit('message', JSON.stringify({
176
- type: 'taskforce:replay',
177
- workspaceId: 'workspace-a',
178
- cursor: 'epoch-1:1'
179
- }));
180
- ws.emit('message', JSON.stringify({
181
- type: 'taskforce:replay',
182
- workspaceId: 'workspace-a',
183
- cursor: 'epoch-1:1'
184
- }));
185
- const replays = parseMessages(ws).filter((msg) => msg.type === 'taskforce:replay');
186
- expect(replays.length).toBeGreaterThanOrEqual(2);
187
- const firstEvents = (replays[0]?.events || []).map((event) => event.eventId);
188
- const secondEvents = (replays[1]?.events || []).map((event) => event.eventId);
189
- expect(firstEvents).toEqual(['epoch-1:2', 'epoch-1:3']);
190
- expect(secondEvents).toEqual(firstEvents);
191
- });
192
- });
@@ -1 +0,0 @@
1
- export {};