@consilioweb/payload-support 2.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +670 -402
  2. package/dist/components/RichTextEditor/index.d.ts +19 -0
  3. package/dist/components/TicketConversation/RewriteDropdown.d.ts +6 -0
  4. package/dist/components/TicketConversation/SkeletonText.d.ts +4 -0
  5. package/dist/components/TicketConversation/components/AISummaryPanel.d.ts +14 -0
  6. package/dist/components/TicketConversation/components/ActionPanels.d.ts +47 -0
  7. package/dist/components/TicketConversation/components/ActivityLog.d.ts +5 -0
  8. package/dist/components/TicketConversation/components/ClientBar.d.ts +5 -0
  9. package/dist/components/TicketConversation/components/ClientHistory.d.ts +24 -0
  10. package/dist/components/TicketConversation/components/CodeBlock.d.ts +26 -0
  11. package/dist/components/TicketConversation/components/CodeBlockInserter.d.ts +12 -0
  12. package/dist/components/TicketConversation/components/QuickActions.d.ts +32 -0
  13. package/dist/components/TicketConversation/components/TicketHeader.d.ts +19 -0
  14. package/dist/components/TicketConversation/components/TimeTrackingPanel.d.ts +24 -0
  15. package/dist/components/TicketConversation/config.d.ts +10 -0
  16. package/dist/components/TicketConversation/config.js +1 -40
  17. package/dist/components/TicketConversation/constants.d.ts +56 -0
  18. package/dist/components/TicketConversation/context.d.ts +53 -0
  19. package/dist/components/TicketConversation/hooks/useAI.d.ts +21 -0
  20. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.d.ts +3 -0
  21. package/dist/components/TicketConversation/hooks/useFeatures.d.ts +23 -0
  22. package/dist/components/TicketConversation/hooks/useFeatures.js +61 -0
  23. package/dist/components/TicketConversation/hooks/useMessageActions.d.ts +20 -0
  24. package/dist/components/TicketConversation/hooks/useReply.d.ts +27 -0
  25. package/dist/components/TicketConversation/hooks/useTicketActions.d.ts +58 -0
  26. package/dist/components/TicketConversation/hooks/useTimeTracking.d.ts +18 -0
  27. package/dist/components/TicketConversation/hooks/useTranslation.d.ts +33 -0
  28. package/dist/components/TicketConversation/index.d.ts +4 -0
  29. package/dist/components/TicketConversation/index.js +2 -2
  30. package/dist/components/TicketConversation/types.d.ts +56 -0
  31. package/dist/components/TicketConversation/utils.d.ts +2 -0
  32. package/dist/index.cjs +940 -404
  33. package/dist/index.d.cts +119 -7
  34. package/dist/index.d.ts +119 -7
  35. package/dist/index.js +936 -405
  36. package/dist/utils/features.d.ts +123 -0
  37. package/dist/utils/features.js +147 -0
  38. package/dist/views/BillingView/client.d.ts +2 -0
  39. package/dist/views/BillingView/index.d.ts +4 -0
  40. package/dist/views/ChatView/client.d.ts +2 -0
  41. package/dist/views/ChatView/client.js +9 -9
  42. package/dist/views/ChatView/index.d.ts +4 -0
  43. package/dist/views/CrmView/client.d.ts +2 -0
  44. package/dist/views/CrmView/client.js +6 -9
  45. package/dist/views/CrmView/index.d.ts +4 -0
  46. package/dist/views/EmailTrackingView/client.d.ts +2 -0
  47. package/dist/views/EmailTrackingView/index.d.ts +4 -0
  48. package/dist/views/ImportConversationView/client.d.ts +2 -0
  49. package/dist/views/ImportConversationView/index.d.ts +4 -0
  50. package/dist/views/LogsView/client.d.ts +2 -0
  51. package/dist/views/LogsView/index.d.ts +4 -0
  52. package/dist/views/NewTicketView/client.d.ts +2 -0
  53. package/dist/views/NewTicketView/index.d.ts +4 -0
  54. package/dist/views/PendingEmailsView/client.d.ts +2 -0
  55. package/dist/views/PendingEmailsView/index.d.ts +4 -0
  56. package/dist/views/SupportDashboardView/client.d.ts +2 -0
  57. package/dist/views/SupportDashboardView/index.d.ts +4 -0
  58. package/dist/views/TicketDetailView/NextActionItem.d.ts +6 -0
  59. package/dist/views/TicketDetailView/RewriteDropdown.d.ts +7 -0
  60. package/dist/views/TicketDetailView/client.d.ts +2 -0
  61. package/dist/views/TicketDetailView/client.js +2 -2
  62. package/dist/views/TicketDetailView/constants.d.ts +13 -0
  63. package/dist/views/TicketDetailView/helpers.d.ts +2 -0
  64. package/dist/views/TicketDetailView/index.d.ts +4 -0
  65. package/dist/views/TicketDetailView/types.d.ts +40 -0
  66. package/dist/views/TicketInboxView/client.d.ts +2 -0
  67. package/dist/views/TicketInboxView/client.js +5 -5
  68. package/dist/views/TicketInboxView/index.d.ts +4 -0
  69. package/dist/views/TicketingSettingsView/client.d.ts +2 -0
  70. package/dist/views/TicketingSettingsView/client.js +5 -4
  71. package/dist/views/TicketingSettingsView/index.d.ts +4 -0
  72. package/dist/views/TimeDashboardView/client.d.ts +2 -0
  73. package/dist/views/TimeDashboardView/index.d.ts +4 -0
  74. package/dist/views/shared/AdminViewHeader.d.ts +13 -0
  75. package/dist/views/shared/ErrorBoundary.d.ts +17 -0
  76. package/dist/views/shared/Skeleton.d.ts +21 -0
  77. package/dist/views/shared/StatusPill.d.ts +8 -0
  78. package/dist/views/shared/adminTokens.d.ts +19 -0
  79. package/dist/views/shared/config.d.ts +8 -0
  80. package/dist/views/shared/config.js +1 -40
  81. package/dist/views/shared/dateLocale.d.ts +5 -0
  82. package/dist/views/shared/index.d.ts +12 -0
  83. package/dist/views/shared/index.js +2 -1
  84. package/dist/views/shared/sla.d.ts +14 -0
  85. package/dist/views.d.ts +13 -13
  86. package/package.json +16 -31
  87. package/src/collections/ChatMessages.ts +59 -2
  88. package/src/collections/ClientSummaries.ts +10 -4
  89. package/src/collections/SupportClients.ts +29 -0
  90. package/src/collections/TicketCollaborators.ts +6 -1
  91. package/src/collections/TicketMessages.ts +49 -24
  92. package/src/collections/Tickets.ts +117 -79
  93. package/src/collections/TimeEntries.ts +57 -32
  94. package/src/collections/WebhookEndpoints.ts +44 -2
  95. package/src/components/TicketConversation/config.ts +16 -80
  96. package/src/components/TicketConversation/hooks/useFeatures.ts +109 -0
  97. package/src/components/TicketConversation/index.tsx +2 -2
  98. package/src/endpoints/admin-chat.ts +3 -3
  99. package/src/endpoints/ai-agent.ts +3 -3
  100. package/src/endpoints/ai.ts +5 -4
  101. package/src/endpoints/auth-2fa.ts +16 -4
  102. package/src/endpoints/capabilities.ts +6 -6
  103. package/src/endpoints/chat.ts +7 -5
  104. package/src/endpoints/chatbot.ts +48 -2
  105. package/src/endpoints/client-intelligence.ts +6 -5
  106. package/src/endpoints/email-stats.ts +19 -3
  107. package/src/endpoints/import-conversation.ts +1 -1
  108. package/src/endpoints/index.ts +1 -1
  109. package/src/endpoints/invite-collaborator.ts +29 -3
  110. package/src/endpoints/login.ts +15 -2
  111. package/src/endpoints/oauth-google.ts +130 -8
  112. package/src/endpoints/process-scheduled.ts +13 -8
  113. package/src/endpoints/resend-notification.ts +3 -3
  114. package/src/endpoints/round-robin-config.ts +30 -20
  115. package/src/endpoints/send-reminder.ts +3 -3
  116. package/src/endpoints/settings.ts +28 -77
  117. package/src/endpoints/signature.ts +9 -2
  118. package/src/endpoints/ticket-synthesis.ts +3 -3
  119. package/src/endpoints/transfer-ticket.ts +28 -3
  120. package/src/endpoints/typing.ts +117 -14
  121. package/src/endpoints/user-prefs.ts +5 -2
  122. package/src/index.ts +6 -2
  123. package/src/plugin.ts +12 -0
  124. package/src/portal/LiveChat.tsx +33 -22
  125. package/src/portal/auth/layout.tsx +19 -1
  126. package/src/portal/auth/tickets/detail/MessageBody.tsx +88 -0
  127. package/src/portal/auth/tickets/detail/TicketPolling.tsx +5 -1
  128. package/src/portal/auth/tickets/detail/page.tsx +2 -6
  129. package/src/portal/login/page.tsx +11 -3
  130. package/src/types/lucide-react.d.ts +10 -2
  131. package/src/utils/aiAgent.ts +2 -1
  132. package/src/utils/aiProvider.ts +21 -0
  133. package/src/utils/features.ts +297 -0
  134. package/src/utils/fireWebhooks.ts +10 -1
  135. package/src/utils/generateTicketSynthesis.ts +2 -1
  136. package/src/utils/index.ts +4 -2
  137. package/src/utils/rateLimiter.ts +39 -5
  138. package/src/utils/readSettings.ts +199 -16
  139. package/src/utils/ticketAccess.ts +16 -1
  140. package/src/utils/twoFactorChallenge.ts +80 -0
  141. package/src/utils/urlSafety.ts +230 -0
  142. package/src/utils/webhookDispatcher.ts +24 -8
  143. package/src/views/ChatView/client.tsx +13 -9
  144. package/src/views/CrmView/client.tsx +10 -11
  145. package/src/views/TicketDetailView/client.tsx +2 -2
  146. package/src/views/TicketInboxView/client.tsx +10 -5
  147. package/src/views/TicketingSettingsView/client.tsx +8 -6
  148. package/src/views/shared/config.ts +14 -80
  149. package/src/views/shared/index.ts +3 -1
  150. package/dist/components/RichTextEditor/index.cjs +0 -279
  151. package/dist/components/TicketConversation/RewriteDropdown.cjs +0 -98
  152. package/dist/components/TicketConversation/SkeletonText.cjs +0 -21
  153. package/dist/components/TicketConversation/components/AISummaryPanel.cjs +0 -85
  154. package/dist/components/TicketConversation/components/ActionPanels.cjs +0 -193
  155. package/dist/components/TicketConversation/components/ActivityLog.cjs +0 -24
  156. package/dist/components/TicketConversation/components/ClientBar.cjs +0 -43
  157. package/dist/components/TicketConversation/components/ClientHistory.cjs +0 -138
  158. package/dist/components/TicketConversation/components/CodeBlock.cjs +0 -171
  159. package/dist/components/TicketConversation/components/CodeBlockInserter.cjs +0 -156
  160. package/dist/components/TicketConversation/components/QuickActions.cjs +0 -87
  161. package/dist/components/TicketConversation/components/TicketHeader.cjs +0 -93
  162. package/dist/components/TicketConversation/components/TimeTrackingPanel.cjs +0 -134
  163. package/dist/components/TicketConversation/config.cjs +0 -44
  164. package/dist/components/TicketConversation/constants.cjs +0 -127
  165. package/dist/components/TicketConversation/context.cjs +0 -13
  166. package/dist/components/TicketConversation/hooks/useAI.cjs +0 -196
  167. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.cjs +0 -20
  168. package/dist/components/TicketConversation/hooks/useMessageActions.cjs +0 -136
  169. package/dist/components/TicketConversation/hooks/useReply.cjs +0 -188
  170. package/dist/components/TicketConversation/hooks/useTicketActions.cjs +0 -290
  171. package/dist/components/TicketConversation/hooks/useTimeTracking.cjs +0 -123
  172. package/dist/components/TicketConversation/hooks/useTranslation.cjs +0 -101
  173. package/dist/components/TicketConversation/index.cjs +0 -1114
  174. package/dist/components/TicketConversation/types.cjs +0 -2
  175. package/dist/components/TicketConversation/utils.cjs +0 -27
  176. package/dist/views/BillingView/client.cjs +0 -469
  177. package/dist/views/BillingView/index.cjs +0 -34
  178. package/dist/views/ChatView/client.cjs +0 -301
  179. package/dist/views/ChatView/index.cjs +0 -34
  180. package/dist/views/CrmView/client.cjs +0 -458
  181. package/dist/views/CrmView/index.cjs +0 -34
  182. package/dist/views/EmailTrackingView/client.cjs +0 -171
  183. package/dist/views/EmailTrackingView/index.cjs +0 -34
  184. package/dist/views/ImportConversationView/client.cjs +0 -238
  185. package/dist/views/ImportConversationView/index.cjs +0 -34
  186. package/dist/views/LogsView/client.cjs +0 -148
  187. package/dist/views/LogsView/index.cjs +0 -32
  188. package/dist/views/NewTicketView/client.cjs +0 -213
  189. package/dist/views/NewTicketView/index.cjs +0 -32
  190. package/dist/views/PendingEmailsView/client.cjs +0 -471
  191. package/dist/views/PendingEmailsView/index.cjs +0 -34
  192. package/dist/views/SupportDashboardView/client.cjs +0 -437
  193. package/dist/views/SupportDashboardView/index.cjs +0 -34
  194. package/dist/views/TicketDetailView/NextActionItem.cjs +0 -40
  195. package/dist/views/TicketDetailView/RewriteDropdown.cjs +0 -80
  196. package/dist/views/TicketDetailView/client.cjs +0 -1412
  197. package/dist/views/TicketDetailView/constants.cjs +0 -20
  198. package/dist/views/TicketDetailView/helpers.cjs +0 -23
  199. package/dist/views/TicketDetailView/index.cjs +0 -34
  200. package/dist/views/TicketDetailView/types.cjs +0 -2
  201. package/dist/views/TicketInboxView/client.cjs +0 -359
  202. package/dist/views/TicketInboxView/index.cjs +0 -32
  203. package/dist/views/TicketingSettingsView/client.cjs +0 -740
  204. package/dist/views/TicketingSettingsView/index.cjs +0 -34
  205. package/dist/views/TimeDashboardView/client.cjs +0 -180
  206. package/dist/views/TimeDashboardView/index.cjs +0 -34
  207. package/dist/views/shared/AdminViewHeader.cjs +0 -68
  208. package/dist/views/shared/ErrorBoundary.cjs +0 -55
  209. package/dist/views/shared/Skeleton.cjs +0 -77
  210. package/dist/views/shared/StatusPill.cjs +0 -47
  211. package/dist/views/shared/adminTokens.cjs +0 -34
  212. package/dist/views/shared/config.cjs +0 -44
  213. package/dist/views/shared/dateLocale.cjs +0 -5
  214. package/dist/views/shared/index.cjs +0 -72
  215. package/dist/views/shared/sla.cjs +0 -37
  216. package/dist/views.cjs +0 -86
  217. package/dist/views.d.cts +0 -13
  218. package/src/__tests__/aiSummaryRendering.test.ts +0 -21
  219. package/src/__tests__/authResponses.test.ts +0 -69
  220. package/src/__tests__/capabilities.test.ts +0 -71
  221. package/src/__tests__/generateTrackingToken.test.ts +0 -67
  222. package/src/__tests__/integration/ai-agent.test.ts +0 -46
  223. package/src/__tests__/integration/auth.test.ts +0 -43
  224. package/src/__tests__/integration/automation-rules.test.ts +0 -68
  225. package/src/__tests__/integration/buildTestPayload.ts +0 -52
  226. package/src/__tests__/integration/channels.test.ts +0 -52
  227. package/src/__tests__/integration/core-behaviors.test.ts +0 -81
  228. package/src/__tests__/integration/dashboard-volume.test.ts +0 -31
  229. package/src/__tests__/integration/digest.test.ts +0 -48
  230. package/src/__tests__/integration/invoice-pdf.test.ts +0 -30
  231. package/src/__tests__/integration/isolation.test.ts +0 -81
  232. package/src/__tests__/integration/mentions-invoice.test.ts +0 -50
  233. package/src/__tests__/integration/nps.test.ts +0 -37
  234. package/src/__tests__/integration/per-team-sla.test.ts +0 -44
  235. package/src/__tests__/integration/push.test.ts +0 -75
  236. package/src/__tests__/integration/sanitization.test.ts +0 -35
  237. package/src/__tests__/integration/sla-pause.test.ts +0 -39
  238. package/src/__tests__/integration/smoke.test.ts +0 -26
  239. package/src/__tests__/integration/snooze.test.ts +0 -68
  240. package/src/__tests__/integration/teams.test.ts +0 -38
  241. package/src/__tests__/rateLimiter.test.ts +0 -83
  242. package/src/__tests__/sanitizeHtml.test.ts +0 -165
  243. package/src/__tests__/sla.test.ts +0 -258
  244. package/src/__tests__/trackOpenEndpoint.test.ts +0 -54
  245. package/src/__tests__/translations.test.ts +0 -67
  246. package/src/__tests__/webhookSecurity.test.ts +0 -58
package/dist/index.d.cts CHANGED
@@ -28,7 +28,19 @@ declare class RateLimiter {
28
28
  private readonly windowMs;
29
29
  private readonly maxRequests;
30
30
  private readonly store;
31
- constructor(windowMs: number, maxRequests: number, store?: RateLimitStore);
31
+ private readonly prefix;
32
+ /**
33
+ * @param namespace Endpoint-scoped prefix for every key this limiter writes.
34
+ * The store is SHARED (`rateLimitStore: 'payload'` builds one instance for
35
+ * all endpoints), and the raw keys collide across endpoints: `ip` was used
36
+ * by both the login and the chatbot limiter — 10 forged chatbot requests
37
+ * locked a victim out of the portal for 15 minutes — and `String(user.id)`
38
+ * by five different endpoints. Always pass one; it is optional only because
39
+ * `RateLimiter` is part of the published API surface.
40
+ */
41
+ constructor(windowMs: number, maxRequests: number, store?: RateLimitStore, namespace?: string);
42
+ /** The key actually written to the store. Exposed for assertions in tests. */
43
+ scopedKey(key: string): string;
32
44
  check(key: string, context?: unknown): Promise<boolean>;
33
45
  reset(key: string, context?: unknown): Promise<void>;
34
46
  }
@@ -381,6 +393,87 @@ declare const DEFAULT_SLUGS: CollectionSlugs;
381
393
  */
382
394
  declare function resolveSlugs(overrides?: Partial<CollectionSlugs>): CollectionSlugs;
383
395
 
396
+ /**
397
+ * Ticketing feature flags — single source of truth (client + server).
398
+ *
399
+ * Flags are stored SERVER-side, in the `support-settings` row of
400
+ * `payload-preferences` (see `readSettings.ts` and `endpoints/settings.ts`).
401
+ * A flag toggled on one machine therefore applies to every browser and to
402
+ * server-side code (hooks, cron endpoints) alike.
403
+ *
404
+ * `localStorage` is still used, but demoted to a plain CACHE: it lets admin
405
+ * views paint synchronously on first render and keeps them usable when the
406
+ * settings endpoint is unreachable. It is never authoritative.
407
+ *
408
+ * Two flags are *projections* of settings that already lived server-side long
409
+ * before this module, and are deliberately NOT stored twice:
410
+ * - `autoClose` ← `settings.autoClose.enabled`
411
+ * - `autoCloseDays` ← `settings.autoClose.daysBeforeClose`
412
+ * The `autoClose` block stays authoritative; the flags are recomputed on read
413
+ * and dropped on write. See `projectAutoClose` / `stripProjectedFeatures`.
414
+ */
415
+ /**
416
+ * Feature flags for the ticketing module.
417
+ * Each feature can be enabled/disabled by the admin.
418
+ * When disabled, the corresponding UI section is hidden entirely.
419
+ */
420
+ interface TicketingFeatures {
421
+ /** Time tracking: timer, manual entries, billing */
422
+ timeTracking: boolean;
423
+ /** AI features: sentiment, synthesis, suggestion, rewrite */
424
+ ai: boolean;
425
+ /** Satisfaction surveys: CSAT rating after resolution */
426
+ satisfaction: boolean;
427
+ /** Live chat integration: chat → ticket conversion */
428
+ chat: boolean;
429
+ /** Email tracking: pixel tracking, open/sent status per message */
430
+ emailTracking: boolean;
431
+ /** Canned responses: quick reply templates */
432
+ canned: boolean;
433
+ /** Ticket merge: combine two tickets into one */
434
+ merge: boolean;
435
+ /** Snooze: temporarily hide a ticket */
436
+ snooze: boolean;
437
+ /** External messages: add messages received outside the system */
438
+ externalMessages: boolean;
439
+ /** Client history: past tickets, projects, notes sidebar */
440
+ clientHistory: boolean;
441
+ /** Activity log: audit trail of actions on the ticket */
442
+ activityLog: boolean;
443
+ /** Split ticket: extract a message into a new ticket */
444
+ splitTicket: boolean;
445
+ /** Scheduled replies: send a message at a future date */
446
+ scheduledReplies: boolean;
447
+ /** Auto-close: automatically resolve inactive tickets (projection of settings.autoClose.enabled) */
448
+ autoClose: boolean;
449
+ /** Auto-close delay in days (projection of settings.autoClose.daysBeforeClose) */
450
+ autoCloseDays: number;
451
+ /** Round-robin: distribute new tickets evenly among agents */
452
+ roundRobin: boolean;
453
+ }
454
+ /** Default features — all enabled except round-robin */
455
+ declare const DEFAULT_TICKETING_FEATURES: TicketingFeatures;
456
+ /** Flags derived from another settings block — never persisted inside `features`. */
457
+ declare const PROJECTED_FEATURE_KEYS: readonly ["autoClose", "autoCloseDays"];
458
+ /**
459
+ * Coerce arbitrary input (stale localStorage, hand-edited preference row, older
460
+ * plugin version) into a complete, well-typed feature set. Unknown keys are
461
+ * dropped and wrongly-typed values fall back to their default.
462
+ */
463
+ declare function normalizeFeatures(raw: unknown): TicketingFeatures;
464
+ /** Shape of the `autoClose` settings block the two projected flags mirror. */
465
+ interface AutoCloseProjectionSource {
466
+ enabled?: boolean;
467
+ daysBeforeClose?: number;
468
+ }
469
+ /** Recompute the projected flags from the authoritative `autoClose` block. */
470
+ declare function projectAutoClose(features: TicketingFeatures, autoClose: AutoCloseProjectionSource | undefined): TicketingFeatures;
471
+ /**
472
+ * Drop the projected flags before persisting, so `features` and the `autoClose`
473
+ * block can never disagree.
474
+ */
475
+ declare function stripProjectedFeatures(features: TicketingFeatures): Omit<TicketingFeatures, (typeof PROJECTED_FEATURE_KEYS)[number]>;
476
+
384
477
  interface SupportSettings {
385
478
  email: {
386
479
  fromAddress: string;
@@ -406,6 +499,8 @@ interface SupportSettings {
406
499
  daysBeforeClose: number;
407
500
  reminderDaysBefore: number;
408
501
  };
502
+ /** Ticketing feature flags — server-authoritative since 2.1 (used to be per-browser localStorage). */
503
+ features: TicketingFeatures;
409
504
  }
410
505
  interface UserPrefs {
411
506
  locale: 'fr' | 'en';
@@ -413,13 +508,26 @@ interface UserPrefs {
413
508
  }
414
509
  declare const DEFAULT_SETTINGS: SupportSettings;
415
510
  declare const DEFAULT_USER_PREFS: UserPrefs;
416
- declare function readSupportSettings(payload: Payload): Promise<SupportSettings>;
417
- declare function readUserPrefs(payload: Payload, userId: string | number): Promise<UserPrefs>;
511
+ interface SupportSettingsState {
512
+ settings: SupportSettings;
513
+ /**
514
+ * False when the preference row carries no `features` object yet — a
515
+ * pre-2.1 install, or a fresh one. Clients use it to decide whether their
516
+ * legacy `localStorage` flags should seed the server.
517
+ */
518
+ featuresConfigured: boolean;
519
+ }
520
+ declare function readSupportSettingsState(payload: Payload, staffSlug?: string): Promise<SupportSettingsState>;
521
+ declare function readSupportSettings(payload: Payload, staffSlug?: string): Promise<SupportSettings>;
522
+ declare function readUserPrefs(payload: Payload, userId: string | number, staffSlug?: string): Promise<UserPrefs>;
418
523
 
419
524
  /**
420
525
  * POST /api/support/oauth/google
421
526
  * Google OAuth — handles both login redirect and callback.
422
- * Body: { action: 'login' } or { code: string, state: string, cookieState: string }
527
+ * Body: { action: 'login' } or { code: string, state: string }
528
+ *
529
+ * The CSRF state is NOT taken from the body: `action: 'login'` issues it as an
530
+ * HttpOnly cookie and the callback reads it back from the `Cookie` header.
423
531
  */
424
532
  interface OAuthGoogleOptions {
425
533
  allowedEmailDomains?: string[];
@@ -455,7 +563,11 @@ declare function createAdminNotification(payload: Payload, data: {
455
563
  recipient?: number | string;
456
564
  }, collectionSlug?: string): Promise<void>;
457
565
 
458
- type WebhookEvent = 'ticket_created' | 'ticket_resolved' | 'ticket_replied' | 'sla_breached';
566
+ /**
567
+ * Outbound webhook events. Must stay in sync with the `events` options of the
568
+ * webhook-endpoints collection.
569
+ */
570
+ type WebhookEvent = 'ticket_created' | 'ticket_resolved' | 'ticket_replied' | 'ticket_assigned' | 'sla_breached';
459
571
  /**
460
572
  * Dispatch outbound webhooks for a given event.
461
573
  * Fetches all active webhook endpoints matching the event,
@@ -467,7 +579,7 @@ type WebhookEvent = 'ticket_created' | 'ticket_resolved' | 'ticket_replied' | 's
467
579
  * @param payload - Payload instance
468
580
  * @param slugs - Collection slugs for dynamic collection references
469
581
  */
470
- declare function dispatchWebhook(data: Record<string, unknown>, event: WebhookEvent, payload: BasePayload, slugs: CollectionSlugs): void;
582
+ declare function dispatchWebhook(data: Record<string, unknown>, event: WebhookEvent, payload: BasePayload, slugs: CollectionSlugs): Promise<void>;
471
583
 
472
584
  interface TicketSynthesisResult {
473
585
  summary: string;
@@ -578,4 +690,4 @@ declare function createTicketStatusesCollection(slugs: CollectionSlugs): Collect
578
690
  */
579
691
  declare function createTicketCollaboratorsCollection(slugs: CollectionSlugs): CollectionConfig;
580
692
 
581
- export { type AIProviderConfig, type ActivityEntryData, type CannedResponseData, type ClientData, type CollectionSlugs, DEFAULT_FEATURES, DEFAULT_INBOUND_EMAIL_LIMITS, DEFAULT_SETTINGS, DEFAULT_SLUGS, DEFAULT_USER_PREFS, type EmailConfig, type InboundEmailInput, type InboundEmailLimits, type InboundEmailValidationError, MemoryRateLimitStore, type MessageData, PayloadRateLimitStore, type RateLimitEntry, type RateLimitStore, RateLimiter, type SatisfactionSurveyData, type SupportFeatures, type SupportPluginConfig, type SupportSettings, type TicketData, type TicketSynthesisResult, type TimeEntryData, type UserPrefs, calculateBusinessHoursDeadline, createAdminNotification, createAssignSlaDeadlines, createAuthLogsCollection, createCannedResponsesCollection, createChatMessagesCollection, createCheckSlaOnReply, createCheckSlaOnResolve, createEmailLogsCollection, createKnowledgeBaseCollection, createLoginEndpoint, createMacrosCollection, createOAuthGoogleEndpoint, createPendingEmailsCollection, createSatisfactionSurveysCollection, createSlaPoliciesCollection, createSupportClientsCollection, createTicketActivityLogCollection, createTicketCollaboratorsCollection, createTicketMessagesCollection, createTicketStatusEmail, createTicketStatusesCollection, createTicketsCollection, createTimeEntriesCollection, createTrackOpenEndpoint, createWebhookEndpointsCollection, dispatchWebhook, generateTicketSynthesis, readSupportSettings, readUserPrefs, resolveSlugs, supportPlugin, validateInboundEmailPayload, verifySecret };
693
+ export { type AIProviderConfig, type ActivityEntryData, type CannedResponseData, type ClientData, type CollectionSlugs, DEFAULT_FEATURES, DEFAULT_INBOUND_EMAIL_LIMITS, DEFAULT_SETTINGS, DEFAULT_SLUGS, DEFAULT_TICKETING_FEATURES, DEFAULT_USER_PREFS, type EmailConfig, type InboundEmailInput, type InboundEmailLimits, type InboundEmailValidationError, MemoryRateLimitStore, type MessageData, PayloadRateLimitStore, type RateLimitEntry, type RateLimitStore, RateLimiter, type SatisfactionSurveyData, type SupportFeatures, type SupportPluginConfig, type SupportSettings, type SupportSettingsState, type TicketData, type TicketSynthesisResult, type TicketingFeatures, type TimeEntryData, type UserPrefs, calculateBusinessHoursDeadline, createAdminNotification, createAssignSlaDeadlines, createAuthLogsCollection, createCannedResponsesCollection, createChatMessagesCollection, createCheckSlaOnReply, createCheckSlaOnResolve, createEmailLogsCollection, createKnowledgeBaseCollection, createLoginEndpoint, createMacrosCollection, createOAuthGoogleEndpoint, createPendingEmailsCollection, createSatisfactionSurveysCollection, createSlaPoliciesCollection, createSupportClientsCollection, createTicketActivityLogCollection, createTicketCollaboratorsCollection, createTicketMessagesCollection, createTicketStatusEmail, createTicketStatusesCollection, createTicketsCollection, createTimeEntriesCollection, createTrackOpenEndpoint, createWebhookEndpointsCollection, dispatchWebhook, generateTicketSynthesis, normalizeFeatures, projectAutoClose, readSupportSettings, readSupportSettingsState, readUserPrefs, resolveSlugs, stripProjectedFeatures, supportPlugin, validateInboundEmailPayload, verifySecret };
package/dist/index.d.ts CHANGED
@@ -28,7 +28,19 @@ declare class RateLimiter {
28
28
  private readonly windowMs;
29
29
  private readonly maxRequests;
30
30
  private readonly store;
31
- constructor(windowMs: number, maxRequests: number, store?: RateLimitStore);
31
+ private readonly prefix;
32
+ /**
33
+ * @param namespace Endpoint-scoped prefix for every key this limiter writes.
34
+ * The store is SHARED (`rateLimitStore: 'payload'` builds one instance for
35
+ * all endpoints), and the raw keys collide across endpoints: `ip` was used
36
+ * by both the login and the chatbot limiter — 10 forged chatbot requests
37
+ * locked a victim out of the portal for 15 minutes — and `String(user.id)`
38
+ * by five different endpoints. Always pass one; it is optional only because
39
+ * `RateLimiter` is part of the published API surface.
40
+ */
41
+ constructor(windowMs: number, maxRequests: number, store?: RateLimitStore, namespace?: string);
42
+ /** The key actually written to the store. Exposed for assertions in tests. */
43
+ scopedKey(key: string): string;
32
44
  check(key: string, context?: unknown): Promise<boolean>;
33
45
  reset(key: string, context?: unknown): Promise<void>;
34
46
  }
@@ -381,6 +393,87 @@ declare const DEFAULT_SLUGS: CollectionSlugs;
381
393
  */
382
394
  declare function resolveSlugs(overrides?: Partial<CollectionSlugs>): CollectionSlugs;
383
395
 
396
+ /**
397
+ * Ticketing feature flags — single source of truth (client + server).
398
+ *
399
+ * Flags are stored SERVER-side, in the `support-settings` row of
400
+ * `payload-preferences` (see `readSettings.ts` and `endpoints/settings.ts`).
401
+ * A flag toggled on one machine therefore applies to every browser and to
402
+ * server-side code (hooks, cron endpoints) alike.
403
+ *
404
+ * `localStorage` is still used, but demoted to a plain CACHE: it lets admin
405
+ * views paint synchronously on first render and keeps them usable when the
406
+ * settings endpoint is unreachable. It is never authoritative.
407
+ *
408
+ * Two flags are *projections* of settings that already lived server-side long
409
+ * before this module, and are deliberately NOT stored twice:
410
+ * - `autoClose` ← `settings.autoClose.enabled`
411
+ * - `autoCloseDays` ← `settings.autoClose.daysBeforeClose`
412
+ * The `autoClose` block stays authoritative; the flags are recomputed on read
413
+ * and dropped on write. See `projectAutoClose` / `stripProjectedFeatures`.
414
+ */
415
+ /**
416
+ * Feature flags for the ticketing module.
417
+ * Each feature can be enabled/disabled by the admin.
418
+ * When disabled, the corresponding UI section is hidden entirely.
419
+ */
420
+ interface TicketingFeatures {
421
+ /** Time tracking: timer, manual entries, billing */
422
+ timeTracking: boolean;
423
+ /** AI features: sentiment, synthesis, suggestion, rewrite */
424
+ ai: boolean;
425
+ /** Satisfaction surveys: CSAT rating after resolution */
426
+ satisfaction: boolean;
427
+ /** Live chat integration: chat → ticket conversion */
428
+ chat: boolean;
429
+ /** Email tracking: pixel tracking, open/sent status per message */
430
+ emailTracking: boolean;
431
+ /** Canned responses: quick reply templates */
432
+ canned: boolean;
433
+ /** Ticket merge: combine two tickets into one */
434
+ merge: boolean;
435
+ /** Snooze: temporarily hide a ticket */
436
+ snooze: boolean;
437
+ /** External messages: add messages received outside the system */
438
+ externalMessages: boolean;
439
+ /** Client history: past tickets, projects, notes sidebar */
440
+ clientHistory: boolean;
441
+ /** Activity log: audit trail of actions on the ticket */
442
+ activityLog: boolean;
443
+ /** Split ticket: extract a message into a new ticket */
444
+ splitTicket: boolean;
445
+ /** Scheduled replies: send a message at a future date */
446
+ scheduledReplies: boolean;
447
+ /** Auto-close: automatically resolve inactive tickets (projection of settings.autoClose.enabled) */
448
+ autoClose: boolean;
449
+ /** Auto-close delay in days (projection of settings.autoClose.daysBeforeClose) */
450
+ autoCloseDays: number;
451
+ /** Round-robin: distribute new tickets evenly among agents */
452
+ roundRobin: boolean;
453
+ }
454
+ /** Default features — all enabled except round-robin */
455
+ declare const DEFAULT_TICKETING_FEATURES: TicketingFeatures;
456
+ /** Flags derived from another settings block — never persisted inside `features`. */
457
+ declare const PROJECTED_FEATURE_KEYS: readonly ["autoClose", "autoCloseDays"];
458
+ /**
459
+ * Coerce arbitrary input (stale localStorage, hand-edited preference row, older
460
+ * plugin version) into a complete, well-typed feature set. Unknown keys are
461
+ * dropped and wrongly-typed values fall back to their default.
462
+ */
463
+ declare function normalizeFeatures(raw: unknown): TicketingFeatures;
464
+ /** Shape of the `autoClose` settings block the two projected flags mirror. */
465
+ interface AutoCloseProjectionSource {
466
+ enabled?: boolean;
467
+ daysBeforeClose?: number;
468
+ }
469
+ /** Recompute the projected flags from the authoritative `autoClose` block. */
470
+ declare function projectAutoClose(features: TicketingFeatures, autoClose: AutoCloseProjectionSource | undefined): TicketingFeatures;
471
+ /**
472
+ * Drop the projected flags before persisting, so `features` and the `autoClose`
473
+ * block can never disagree.
474
+ */
475
+ declare function stripProjectedFeatures(features: TicketingFeatures): Omit<TicketingFeatures, (typeof PROJECTED_FEATURE_KEYS)[number]>;
476
+
384
477
  interface SupportSettings {
385
478
  email: {
386
479
  fromAddress: string;
@@ -406,6 +499,8 @@ interface SupportSettings {
406
499
  daysBeforeClose: number;
407
500
  reminderDaysBefore: number;
408
501
  };
502
+ /** Ticketing feature flags — server-authoritative since 2.1 (used to be per-browser localStorage). */
503
+ features: TicketingFeatures;
409
504
  }
410
505
  interface UserPrefs {
411
506
  locale: 'fr' | 'en';
@@ -413,13 +508,26 @@ interface UserPrefs {
413
508
  }
414
509
  declare const DEFAULT_SETTINGS: SupportSettings;
415
510
  declare const DEFAULT_USER_PREFS: UserPrefs;
416
- declare function readSupportSettings(payload: Payload): Promise<SupportSettings>;
417
- declare function readUserPrefs(payload: Payload, userId: string | number): Promise<UserPrefs>;
511
+ interface SupportSettingsState {
512
+ settings: SupportSettings;
513
+ /**
514
+ * False when the preference row carries no `features` object yet — a
515
+ * pre-2.1 install, or a fresh one. Clients use it to decide whether their
516
+ * legacy `localStorage` flags should seed the server.
517
+ */
518
+ featuresConfigured: boolean;
519
+ }
520
+ declare function readSupportSettingsState(payload: Payload, staffSlug?: string): Promise<SupportSettingsState>;
521
+ declare function readSupportSettings(payload: Payload, staffSlug?: string): Promise<SupportSettings>;
522
+ declare function readUserPrefs(payload: Payload, userId: string | number, staffSlug?: string): Promise<UserPrefs>;
418
523
 
419
524
  /**
420
525
  * POST /api/support/oauth/google
421
526
  * Google OAuth — handles both login redirect and callback.
422
- * Body: { action: 'login' } or { code: string, state: string, cookieState: string }
527
+ * Body: { action: 'login' } or { code: string, state: string }
528
+ *
529
+ * The CSRF state is NOT taken from the body: `action: 'login'` issues it as an
530
+ * HttpOnly cookie and the callback reads it back from the `Cookie` header.
423
531
  */
424
532
  interface OAuthGoogleOptions {
425
533
  allowedEmailDomains?: string[];
@@ -455,7 +563,11 @@ declare function createAdminNotification(payload: Payload, data: {
455
563
  recipient?: number | string;
456
564
  }, collectionSlug?: string): Promise<void>;
457
565
 
458
- type WebhookEvent = 'ticket_created' | 'ticket_resolved' | 'ticket_replied' | 'sla_breached';
566
+ /**
567
+ * Outbound webhook events. Must stay in sync with the `events` options of the
568
+ * webhook-endpoints collection.
569
+ */
570
+ type WebhookEvent = 'ticket_created' | 'ticket_resolved' | 'ticket_replied' | 'ticket_assigned' | 'sla_breached';
459
571
  /**
460
572
  * Dispatch outbound webhooks for a given event.
461
573
  * Fetches all active webhook endpoints matching the event,
@@ -467,7 +579,7 @@ type WebhookEvent = 'ticket_created' | 'ticket_resolved' | 'ticket_replied' | 's
467
579
  * @param payload - Payload instance
468
580
  * @param slugs - Collection slugs for dynamic collection references
469
581
  */
470
- declare function dispatchWebhook(data: Record<string, unknown>, event: WebhookEvent, payload: BasePayload, slugs: CollectionSlugs): void;
582
+ declare function dispatchWebhook(data: Record<string, unknown>, event: WebhookEvent, payload: BasePayload, slugs: CollectionSlugs): Promise<void>;
471
583
 
472
584
  interface TicketSynthesisResult {
473
585
  summary: string;
@@ -578,4 +690,4 @@ declare function createTicketStatusesCollection(slugs: CollectionSlugs): Collect
578
690
  */
579
691
  declare function createTicketCollaboratorsCollection(slugs: CollectionSlugs): CollectionConfig;
580
692
 
581
- export { type AIProviderConfig, type ActivityEntryData, type CannedResponseData, type ClientData, type CollectionSlugs, DEFAULT_FEATURES, DEFAULT_INBOUND_EMAIL_LIMITS, DEFAULT_SETTINGS, DEFAULT_SLUGS, DEFAULT_USER_PREFS, type EmailConfig, type InboundEmailInput, type InboundEmailLimits, type InboundEmailValidationError, MemoryRateLimitStore, type MessageData, PayloadRateLimitStore, type RateLimitEntry, type RateLimitStore, RateLimiter, type SatisfactionSurveyData, type SupportFeatures, type SupportPluginConfig, type SupportSettings, type TicketData, type TicketSynthesisResult, type TimeEntryData, type UserPrefs, calculateBusinessHoursDeadline, createAdminNotification, createAssignSlaDeadlines, createAuthLogsCollection, createCannedResponsesCollection, createChatMessagesCollection, createCheckSlaOnReply, createCheckSlaOnResolve, createEmailLogsCollection, createKnowledgeBaseCollection, createLoginEndpoint, createMacrosCollection, createOAuthGoogleEndpoint, createPendingEmailsCollection, createSatisfactionSurveysCollection, createSlaPoliciesCollection, createSupportClientsCollection, createTicketActivityLogCollection, createTicketCollaboratorsCollection, createTicketMessagesCollection, createTicketStatusEmail, createTicketStatusesCollection, createTicketsCollection, createTimeEntriesCollection, createTrackOpenEndpoint, createWebhookEndpointsCollection, dispatchWebhook, generateTicketSynthesis, readSupportSettings, readUserPrefs, resolveSlugs, supportPlugin, validateInboundEmailPayload, verifySecret };
693
+ export { type AIProviderConfig, type ActivityEntryData, type CannedResponseData, type ClientData, type CollectionSlugs, DEFAULT_FEATURES, DEFAULT_INBOUND_EMAIL_LIMITS, DEFAULT_SETTINGS, DEFAULT_SLUGS, DEFAULT_TICKETING_FEATURES, DEFAULT_USER_PREFS, type EmailConfig, type InboundEmailInput, type InboundEmailLimits, type InboundEmailValidationError, MemoryRateLimitStore, type MessageData, PayloadRateLimitStore, type RateLimitEntry, type RateLimitStore, RateLimiter, type SatisfactionSurveyData, type SupportFeatures, type SupportPluginConfig, type SupportSettings, type SupportSettingsState, type TicketData, type TicketSynthesisResult, type TicketingFeatures, type TimeEntryData, type UserPrefs, calculateBusinessHoursDeadline, createAdminNotification, createAssignSlaDeadlines, createAuthLogsCollection, createCannedResponsesCollection, createChatMessagesCollection, createCheckSlaOnReply, createCheckSlaOnResolve, createEmailLogsCollection, createKnowledgeBaseCollection, createLoginEndpoint, createMacrosCollection, createOAuthGoogleEndpoint, createPendingEmailsCollection, createSatisfactionSurveysCollection, createSlaPoliciesCollection, createSupportClientsCollection, createTicketActivityLogCollection, createTicketCollaboratorsCollection, createTicketMessagesCollection, createTicketStatusEmail, createTicketStatusesCollection, createTicketsCollection, createTimeEntriesCollection, createTrackOpenEndpoint, createWebhookEndpointsCollection, dispatchWebhook, generateTicketSynthesis, normalizeFeatures, projectAutoClose, readSupportSettings, readSupportSettingsState, readUserPrefs, resolveSlugs, stripProjectedFeatures, supportPlugin, validateInboundEmailPayload, verifySecret };