@actuate-media/cms-core 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/api/notifications-routes.test.js +45 -0
- package/dist/__tests__/api/notifications-routes.test.js.map +1 -1
- package/dist/__tests__/api/users-search.test.d.ts +2 -0
- package/dist/__tests__/api/users-search.test.d.ts.map +1 -0
- package/dist/__tests__/api/users-search.test.js +182 -0
- package/dist/__tests__/api/users-search.test.js.map +1 -0
- package/dist/__tests__/realtime/notification-bus.test.d.ts +2 -0
- package/dist/__tests__/realtime/notification-bus.test.d.ts.map +1 -0
- package/dist/__tests__/realtime/notification-bus.test.js +79 -0
- package/dist/__tests__/realtime/notification-bus.test.js.map +1 -0
- package/dist/__tests__/realtime/notification-stream.test.d.ts +2 -0
- package/dist/__tests__/realtime/notification-stream.test.d.ts.map +1 -0
- package/dist/__tests__/realtime/notification-stream.test.js +157 -0
- package/dist/__tests__/realtime/notification-stream.test.js.map +1 -0
- package/dist/__tests__/realtime/notifications.test.js +14 -1
- package/dist/__tests__/realtime/notifications.test.js.map +1 -1
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +97 -3
- package/dist/api/handlers.js.map +1 -1
- package/dist/preview/sse-stream.d.ts +2 -5
- package/dist/preview/sse-stream.d.ts.map +1 -1
- package/dist/preview/sse-stream.js +7 -16
- package/dist/preview/sse-stream.js.map +1 -1
- package/dist/realtime/index.d.ts +3 -0
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js +9 -0
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime/notification-bus.d.ts +60 -0
- package/dist/realtime/notification-bus.d.ts.map +1 -0
- package/dist/realtime/notification-bus.js +77 -0
- package/dist/realtime/notification-bus.js.map +1 -0
- package/dist/realtime/notification-stream.d.ts +50 -0
- package/dist/realtime/notification-stream.d.ts.map +1 -0
- package/dist/realtime/notification-stream.js +148 -0
- package/dist/realtime/notification-stream.js.map +1 -0
- package/dist/realtime/notifications.d.ts +0 -8
- package/dist/realtime/notifications.d.ts.map +1 -1
- package/dist/realtime/notifications.js +28 -4
- package/dist/realtime/notifications.js.map +1 -1
- package/dist/realtime/sse.d.ts +20 -0
- package/dist/realtime/sse.d.ts.map +1 -0
- package/dist/realtime/sse.js +31 -0
- package/dist/realtime/sse.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { PreviewSession } from './index.js';
|
|
2
|
+
import { encodeSseEvent } from '../realtime/sse.js';
|
|
3
|
+
export { encodeSseEvent };
|
|
2
4
|
/**
|
|
3
5
|
* Default keepalive interval. 25 s sits safely under the most aggressive
|
|
4
6
|
* proxy/idle timeouts we've encountered in production (Cloudflare 30 s,
|
|
@@ -18,11 +20,6 @@ export interface PreviewStreamOptions {
|
|
|
18
20
|
/** Override for tests so we don't tie up the suite for 4 minutes. */
|
|
19
21
|
maxLifetimeMs?: number;
|
|
20
22
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Encode a single SSE message. `event` is optional; when omitted the
|
|
23
|
-
* default `message` event is used.
|
|
24
|
-
*/
|
|
25
|
-
export declare function encodeSseEvent(data: unknown, event?: string): string;
|
|
26
23
|
/**
|
|
27
24
|
* Build a `ReadableStream` of Server-Sent Events for the given preview
|
|
28
25
|
* session. The stream:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse-stream.d.ts","sourceRoot":"","sources":["../../src/preview/sse-stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"sse-stream.d.ts","sourceRoot":"","sources":["../../src/preview/sse-stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnD,OAAO,EAAE,cAAc,EAAE,CAAA;AAezB;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QAAS,CAAA;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,QAAgB,CAAA;AAExD,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,YAAY,CAAC,EAC1D,IAAI,GAAE,oBAAyB,GAC9B,cAAc,CAAC,UAAU,CAAC,CAwD5B;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOtD,CAAA;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,YAAY,CAAC,EAC1D,IAAI,CAAC,EAAE,oBAAoB,GAC1B,QAAQ,CAKV"}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { subscribePreviewUpdates } from './event-bus.js';
|
|
2
|
+
import { encodeSseEvent } from '../realtime/sse.js';
|
|
3
|
+
export { encodeSseEvent };
|
|
2
4
|
/**
|
|
3
5
|
* SSE wire format helpers. We keep them small and pure so the route handler
|
|
4
6
|
* can build a `ReadableStream<Uint8Array>` without pulling in any extra
|
|
5
7
|
* dependencies (this also keeps the file edge-compatible).
|
|
8
|
+
*
|
|
9
|
+
* The encoder itself lives in `../realtime/sse.ts` — a single source of
|
|
10
|
+
* truth across every SSE channel in cms-core (preview + notifications,
|
|
11
|
+
* today). Re-exported here so existing callers (and tests) that import
|
|
12
|
+
* it from this module keep working.
|
|
6
13
|
*/
|
|
7
14
|
const encoder = new TextEncoder();
|
|
8
15
|
/**
|
|
@@ -18,22 +25,6 @@ export const PREVIEW_SSE_HEARTBEAT_MS = 25_000;
|
|
|
18
25
|
* few minutes regardless of activity.
|
|
19
26
|
*/
|
|
20
27
|
export const PREVIEW_SSE_MAX_LIFETIME_MS = 4 * 60 * 1000;
|
|
21
|
-
/**
|
|
22
|
-
* Encode a single SSE message. `event` is optional; when omitted the
|
|
23
|
-
* default `message` event is used.
|
|
24
|
-
*/
|
|
25
|
-
export function encodeSseEvent(data, event) {
|
|
26
|
-
const lines = [];
|
|
27
|
-
if (event)
|
|
28
|
-
lines.push(`event: ${event}`);
|
|
29
|
-
const serialized = typeof data === 'string' ? data : JSON.stringify(data);
|
|
30
|
-
for (const line of serialized.split('\n')) {
|
|
31
|
-
lines.push(`data: ${line}`);
|
|
32
|
-
}
|
|
33
|
-
lines.push('');
|
|
34
|
-
lines.push('');
|
|
35
|
-
return lines.join('\n');
|
|
36
|
-
}
|
|
37
28
|
/**
|
|
38
29
|
* Build a `ReadableStream` of Server-Sent Events for the given preview
|
|
39
30
|
* session. The stream:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse-stream.js","sourceRoot":"","sources":["../../src/preview/sse-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"sse-stream.js","sourceRoot":"","sources":["../../src/preview/sse-stream.ts"],"names":[],"mappings":"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"}
|
package/dist/realtime/index.d.ts
CHANGED
|
@@ -15,4 +15,7 @@ export { createRealtimeGateway, type AttachRealtimeGatewayOptions, type Authenti
|
|
|
15
15
|
export { createPrismaDocumentPersistence, type PrismaDocumentCRDTClient, type PrismaDocumentPersistenceOptions, } from './persistence.js';
|
|
16
16
|
export { createComment, deleteComment, listComments, normaliseBody, parseAnchor, reopenComment, resolveComment, toCommentDTO, updateComment, DEFAULT_COMMENT_LIST_PAGE_SIZE, MAX_COMMENT_BODY_LENGTH, MAX_COMMENT_LIST_PAGE_SIZE, type CommentAnchor, type CommentDTO, type CommentsDB, type CommentsError, type CommentsResult, type CreateCommentInput, type DocumentCommentRow, type ListCommentsFilter, type UpdateCommentInput, } from './comments.js';
|
|
17
17
|
export { createNotification, excerptForNotification, extractMentionedUserIds, isNotificationKind, listForUser as listNotificationsForUser, markAllRead as markAllNotificationsRead, markRead as markNotificationRead, toNotificationDTO, unreadCount as notificationsUnreadCount, DEFAULT_NOTIFICATION_LIST_PAGE_SIZE, MAX_NOTIFICATION_LIST_PAGE_SIZE, type CreateNotificationInput, type DocumentNotificationRow, type ListNotificationsFilter, type NotificationDTO, type NotificationKind, type NotificationPayload, type NotificationsDB, type NotificationsError, type NotificationsResult, } from './notifications.js';
|
|
18
|
+
export { publishNotification, subscribeUserNotifications, notificationSubscriberCount, type NotificationListener, } from './notification-bus.js';
|
|
19
|
+
export { createNotificationStream, createNotificationStreamResponse, NOTIFICATION_SSE_HEADERS, NOTIFICATION_SSE_HEARTBEAT_MS, NOTIFICATION_SSE_MAX_LIFETIME_MS, type NotificationStreamOptions, } from './notification-stream.js';
|
|
20
|
+
export { encodeSseEvent } from './sse.js';
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,+BAA+B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,gCAAgC,GACtC,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,8BAA8B,EAC9B,uBAAuB,EACvB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,WAAW,IAAI,wBAAwB,EACvC,WAAW,IAAI,wBAAwB,EACvC,QAAQ,IAAI,oBAAoB,EAChC,iBAAiB,EACjB,WAAW,IAAI,wBAAwB,EACvC,mCAAmC,EACnC,+BAA+B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,+BAA+B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,gCAAgC,GACtC,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,8BAA8B,EAC9B,uBAAuB,EACvB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,WAAW,IAAI,wBAAwB,EACvC,WAAW,IAAI,wBAAwB,EACvC,QAAQ,IAAI,oBAAoB,EAChC,iBAAiB,EACjB,WAAW,IAAI,wBAAwB,EACvC,mCAAmC,EACnC,+BAA+B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,2BAA2B,EAC3B,KAAK,oBAAoB,GAC1B,MAAM,uBAAuB,CAAA;AAM9B,OAAO,EACL,wBAAwB,EACxB,gCAAgC,EAChC,wBAAwB,EACxB,6BAA6B,EAC7B,gCAAgC,EAChC,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAA;AAIjC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
|
package/dist/realtime/index.js
CHANGED
|
@@ -15,4 +15,13 @@ export { createRealtimeGateway, } from './gateway.js';
|
|
|
15
15
|
export { createPrismaDocumentPersistence, } from './persistence.js';
|
|
16
16
|
export { createComment, deleteComment, listComments, normaliseBody, parseAnchor, reopenComment, resolveComment, toCommentDTO, updateComment, DEFAULT_COMMENT_LIST_PAGE_SIZE, MAX_COMMENT_BODY_LENGTH, MAX_COMMENT_LIST_PAGE_SIZE, } from './comments.js';
|
|
17
17
|
export { createNotification, excerptForNotification, extractMentionedUserIds, isNotificationKind, listForUser as listNotificationsForUser, markAllRead as markAllNotificationsRead, markRead as markNotificationRead, toNotificationDTO, unreadCount as notificationsUnreadCount, DEFAULT_NOTIFICATION_LIST_PAGE_SIZE, MAX_NOTIFICATION_LIST_PAGE_SIZE, } from './notifications.js';
|
|
18
|
+
export { publishNotification, subscribeUserNotifications, notificationSubscriberCount, } from './notification-bus.js';
|
|
19
|
+
// `__resetNotificationBus` is intentionally NOT re-exported here. It
|
|
20
|
+
// is a test helper — tests import it directly from
|
|
21
|
+
// `./notification-bus.js` so production consumers can't reach it via
|
|
22
|
+
// the package's public realtime surface.
|
|
23
|
+
export { createNotificationStream, createNotificationStreamResponse, NOTIFICATION_SSE_HEADERS, NOTIFICATION_SSE_HEARTBEAT_MS, NOTIFICATION_SSE_MAX_LIFETIME_MS, } from './notification-stream.js';
|
|
24
|
+
// Shared SSE encoder. Single source of truth across every Server-Sent
|
|
25
|
+
// Events stream in cms-core (preview + notifications).
|
|
26
|
+
export { encodeSseEvent } from './sse.js';
|
|
18
27
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,qBAAqB,GAMtB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,+BAA+B,GAGhC,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,8BAA8B,EAC9B,uBAAuB,EACvB,0BAA0B,GAU3B,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,WAAW,IAAI,wBAAwB,EACvC,WAAW,IAAI,wBAAwB,EACvC,QAAQ,IAAI,oBAAoB,EAChC,iBAAiB,EACjB,WAAW,IAAI,wBAAwB,EACvC,mCAAmC,EACnC,+BAA+B,GAUhC,MAAM,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,qBAAqB,GAMtB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,+BAA+B,GAGhC,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,8BAA8B,EAC9B,uBAAuB,EACvB,0BAA0B,GAU3B,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,WAAW,IAAI,wBAAwB,EACvC,WAAW,IAAI,wBAAwB,EACvC,QAAQ,IAAI,oBAAoB,EAChC,iBAAiB,EACjB,WAAW,IAAI,wBAAwB,EACvC,mCAAmC,EACnC,+BAA+B,GAUhC,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,2BAA2B,GAE5B,MAAM,uBAAuB,CAAA;AAC9B,qEAAqE;AACrE,mDAAmD;AACnD,qEAAqE;AACrE,yCAAyC;AAEzC,OAAO,EACL,wBAAwB,EACxB,gCAAgC,EAChC,wBAAwB,EACxB,6BAA6B,EAC7B,gCAAgC,GAEjC,MAAM,0BAA0B,CAAA;AAEjC,sEAAsE;AACtE,uDAAuD;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process pub/sub for per-user notification events.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the design of `preview/event-bus.ts` but keys subscriptions
|
|
5
|
+
* by `userId` instead of `(collection, documentId)`. When the comments
|
|
6
|
+
* hook layer persists a new `DocumentNotification`, it also calls
|
|
7
|
+
* `publishNotification(userId, dto)` so any active SSE stream for that
|
|
8
|
+
* user receives the row in real time. The HTTP layer still works for
|
|
9
|
+
* clients that don't subscribe — the bus is strictly additive.
|
|
10
|
+
*
|
|
11
|
+
* Why in-process:
|
|
12
|
+
*
|
|
13
|
+
* - Single-node deployments get push-to-bell delivery "for free" with
|
|
14
|
+
* zero infrastructure dependency. This is the dominant deployment
|
|
15
|
+
* shape for self-hosted Actuate today.
|
|
16
|
+
* - Multi-node deployments lose cross-node delivery — node A only
|
|
17
|
+
* pushes to its own subscribers. The poll fallback in the admin UI
|
|
18
|
+
* keeps the bell correct on those nodes; SSE is treated as a
|
|
19
|
+
* latency-win optimisation, not a correctness primitive. A future
|
|
20
|
+
* slice can swap this for a Redis/Postgres-listen-backed bus
|
|
21
|
+
* without changing the public contract (`publishNotification` /
|
|
22
|
+
* `subscribeUserNotifications`).
|
|
23
|
+
*
|
|
24
|
+
* Memory safety: the per-user `Set<Listener>` is dropped from the map
|
|
25
|
+
* when the last subscriber unsubscribes, so an idle process holding
|
|
26
|
+
* thousands of users does not pay any storage cost.
|
|
27
|
+
*/
|
|
28
|
+
import type { NotificationDTO } from './notifications.js';
|
|
29
|
+
export type NotificationListener = (notification: NotificationDTO) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Register a listener for `userId`'s notifications. Returns an
|
|
32
|
+
* unsubscribe function — callers MUST invoke it on disconnect, or the
|
|
33
|
+
* bus will leak listener references indefinitely (each listener pins
|
|
34
|
+
* its enclosing closure).
|
|
35
|
+
*/
|
|
36
|
+
export declare function subscribeUserNotifications(userId: string, listener: NotificationListener): () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Publish `notification` to every listener registered for its
|
|
39
|
+
* `userId`. Listener exceptions are isolated — one buggy stream must
|
|
40
|
+
* not take down every other subscriber for the same user (and never
|
|
41
|
+
* the calling write path).
|
|
42
|
+
*
|
|
43
|
+
* Returns `true` when at least one listener was reachable, `false`
|
|
44
|
+
* otherwise (useful in tests to assert SSE wiring; production code can
|
|
45
|
+
* ignore the return value).
|
|
46
|
+
*/
|
|
47
|
+
export declare function publishNotification(notification: NotificationDTO): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* How many active subscribers does this user have? Useful for tests
|
|
50
|
+
* and operator dashboards ("is anyone listening?"). Not part of the
|
|
51
|
+
* write-path contract.
|
|
52
|
+
*/
|
|
53
|
+
export declare function notificationSubscriberCount(userId: string): number;
|
|
54
|
+
/**
|
|
55
|
+
* Test helper. Drops every subscription. Production code never calls
|
|
56
|
+
* this — listener leaks should be visible in the test suite, not
|
|
57
|
+
* papered over in production.
|
|
58
|
+
*/
|
|
59
|
+
export declare function __resetNotificationBus(): void;
|
|
60
|
+
//# sourceMappingURL=notification-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-bus.d.ts","sourceRoot":"","sources":["../../src/realtime/notification-bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEzD,MAAM,MAAM,oBAAoB,GAAG,CAAC,YAAY,EAAE,eAAe,KAAK,IAAI,CAAA;AAI1E;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,oBAAoB,GAC7B,MAAM,IAAI,CAmBZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,eAAe,GAAG,OAAO,CAkB1E;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const listenersByUser = new Map();
|
|
2
|
+
/**
|
|
3
|
+
* Register a listener for `userId`'s notifications. Returns an
|
|
4
|
+
* unsubscribe function — callers MUST invoke it on disconnect, or the
|
|
5
|
+
* bus will leak listener references indefinitely (each listener pins
|
|
6
|
+
* its enclosing closure).
|
|
7
|
+
*/
|
|
8
|
+
export function subscribeUserNotifications(userId, listener) {
|
|
9
|
+
if (!userId) {
|
|
10
|
+
// Defensive: an empty userId would silently bucket every "unknown"
|
|
11
|
+
// subscriber into the same set, leaking notifications across
|
|
12
|
+
// sessions. Reject at the entry point.
|
|
13
|
+
throw new Error('subscribeUserNotifications requires a non-empty userId');
|
|
14
|
+
}
|
|
15
|
+
let set = listenersByUser.get(userId);
|
|
16
|
+
if (!set) {
|
|
17
|
+
set = new Set();
|
|
18
|
+
listenersByUser.set(userId, set);
|
|
19
|
+
}
|
|
20
|
+
set.add(listener);
|
|
21
|
+
return () => {
|
|
22
|
+
const current = listenersByUser.get(userId);
|
|
23
|
+
if (!current)
|
|
24
|
+
return;
|
|
25
|
+
current.delete(listener);
|
|
26
|
+
if (current.size === 0)
|
|
27
|
+
listenersByUser.delete(userId);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Publish `notification` to every listener registered for its
|
|
32
|
+
* `userId`. Listener exceptions are isolated — one buggy stream must
|
|
33
|
+
* not take down every other subscriber for the same user (and never
|
|
34
|
+
* the calling write path).
|
|
35
|
+
*
|
|
36
|
+
* Returns `true` when at least one listener was reachable, `false`
|
|
37
|
+
* otherwise (useful in tests to assert SSE wiring; production code can
|
|
38
|
+
* ignore the return value).
|
|
39
|
+
*/
|
|
40
|
+
export function publishNotification(notification) {
|
|
41
|
+
// The DTO's `userId` is the source of truth; we deliberately don't
|
|
42
|
+
// accept a separate userId arg so callers can't accidentally publish
|
|
43
|
+
// to the wrong channel (e.g. publishing reply notifications to the
|
|
44
|
+
// actor instead of the recipient).
|
|
45
|
+
const set = listenersByUser.get(notification.userId);
|
|
46
|
+
if (!set || set.size === 0)
|
|
47
|
+
return false;
|
|
48
|
+
// Snapshot the listener set before iteration — a listener may
|
|
49
|
+
// synchronously unsubscribe (e.g. close its stream on the first
|
|
50
|
+
// event), and mutating the Set during forEach would skip neighbours.
|
|
51
|
+
for (const listener of [...set]) {
|
|
52
|
+
try {
|
|
53
|
+
listener(notification);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* isolated; see contract above */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* How many active subscribers does this user have? Useful for tests
|
|
63
|
+
* and operator dashboards ("is anyone listening?"). Not part of the
|
|
64
|
+
* write-path contract.
|
|
65
|
+
*/
|
|
66
|
+
export function notificationSubscriberCount(userId) {
|
|
67
|
+
return listenersByUser.get(userId)?.size ?? 0;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Test helper. Drops every subscription. Production code never calls
|
|
71
|
+
* this — listener leaks should be visible in the test suite, not
|
|
72
|
+
* papered over in production.
|
|
73
|
+
*/
|
|
74
|
+
export function __resetNotificationBus() {
|
|
75
|
+
listenersByUser.clear();
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=notification-bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-bus.js","sourceRoot":"","sources":["../../src/realtime/notification-bus.ts"],"names":[],"mappings":"AA+BA,MAAM,eAAe,GAAG,IAAI,GAAG,EAAqC,CAAA;AAEpE;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAc,EACd,QAA8B;IAE9B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,mEAAmE;QACnE,6DAA6D;QAC7D,uCAAuC;QACvC,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAC3E,CAAC;IACD,IAAI,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACrC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAA;QACf,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAClC,CAAC;IACD,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACjB,OAAO,GAAG,EAAE;QACV,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC3C,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACxB,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;YAAE,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACxD,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAA6B;IAC/D,mEAAmE;IACnE,qEAAqE;IACrE,mEAAmE;IACnE,mCAAmC;IACnC,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IACpD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACxC,8DAA8D;IAC9D,gEAAgE;IAChE,qEAAqE;IACrE,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,QAAQ,CAAC,YAAY,CAAC,CAAA;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAc;IACxD,OAAO,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,IAAI,CAAC,CAAA;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACpC,eAAe,CAAC,KAAK,EAAE,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { encodeSseEvent } from './sse.js';
|
|
2
|
+
export { encodeSseEvent };
|
|
3
|
+
/**
|
|
4
|
+
* Heartbeat interval. 25 s sits safely under the most aggressive
|
|
5
|
+
* proxy/idle timeouts we've encountered in production (Cloudflare 30 s,
|
|
6
|
+
* Vercel Serverless 30 s, most reverse-proxies 60 s). Identical to the
|
|
7
|
+
* preview stream's value so both channels behave the same way under
|
|
8
|
+
* the same proxy fleet.
|
|
9
|
+
*/
|
|
10
|
+
export declare const NOTIFICATION_SSE_HEARTBEAT_MS = 25000;
|
|
11
|
+
/**
|
|
12
|
+
* Maximum stream lifetime in ms. After this, the server closes the
|
|
13
|
+
* stream cleanly so `EventSource` reconnects — important because some
|
|
14
|
+
* platforms (Vercel Fluid Compute, Cloudflare Workers) cap any single
|
|
15
|
+
* response at a few minutes regardless of activity.
|
|
16
|
+
*/
|
|
17
|
+
export declare const NOTIFICATION_SSE_MAX_LIFETIME_MS: number;
|
|
18
|
+
export interface NotificationStreamOptions {
|
|
19
|
+
/** Override for tests so we don't have to wait 25 seconds. */
|
|
20
|
+
heartbeatMs?: number;
|
|
21
|
+
/** Override for tests so we don't tie up the suite for 4 minutes. */
|
|
22
|
+
maxLifetimeMs?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build a `ReadableStream<Uint8Array>` of notification SSE events for
|
|
26
|
+
* the given userId. See the file header for the wire format.
|
|
27
|
+
*
|
|
28
|
+
* The stream:
|
|
29
|
+
*
|
|
30
|
+
* 1. Emits the `ready` frame.
|
|
31
|
+
* 2. Subscribes to the notification bus for `userId`.
|
|
32
|
+
* 3. Forwards every `publishNotification(...)` to the client.
|
|
33
|
+
* 4. Sends heartbeats every `heartbeatMs`.
|
|
34
|
+
* 5. Closes cleanly after `maxLifetimeMs`, letting `EventSource`
|
|
35
|
+
* auto-reconnect (the browser does this for us at the HTTP/2
|
|
36
|
+
* stream layer).
|
|
37
|
+
*
|
|
38
|
+
* On `cancel()` (client disconnect or `controller.close()`), every
|
|
39
|
+
* timer is cleared and the bus subscription is dropped — no leaked
|
|
40
|
+
* listener after a hang-up.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createNotificationStream(userId: string, opts?: NotificationStreamOptions): ReadableStream<Uint8Array>;
|
|
43
|
+
/**
|
|
44
|
+
* Standard SSE response headers. Identical to the preview stream's
|
|
45
|
+
* header set — same proxy fleet, same X-Accel-Buffering disable, same
|
|
46
|
+
* cache-control directives.
|
|
47
|
+
*/
|
|
48
|
+
export declare const NOTIFICATION_SSE_HEADERS: Record<string, string>;
|
|
49
|
+
export declare function createNotificationStreamResponse(userId: string, opts?: NotificationStreamOptions): Response;
|
|
50
|
+
//# sourceMappingURL=notification-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-stream.d.ts","sourceRoot":"","sources":["../../src/realtime/notification-stream.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAEzC,OAAO,EAAE,cAAc,EAAE,CAAA;AAIzB;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,QAAS,CAAA;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,QAAgB,CAAA;AAE7D,MAAM,WAAW,yBAAyB;IACxC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,yBAA8B,GACnC,cAAc,CAAC,UAAU,CAAC,CAmE5B;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO3D,CAAA;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,yBAAyB,GAC/B,QAAQ,CAKV"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-user notification SSE stream.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `preview/sse-stream.ts` for the notification bus: subscribe
|
|
5
|
+
* to the user's channel, forward every `NotificationDTO` to the
|
|
6
|
+
* connected `EventSource` client, emit periodic heartbeats so proxies
|
|
7
|
+
* don't kill the idle connection, and close cleanly after a bounded
|
|
8
|
+
* lifetime so the client reconnects before platform max-duration caps
|
|
9
|
+
* fire.
|
|
10
|
+
*
|
|
11
|
+
* Wire format:
|
|
12
|
+
*
|
|
13
|
+
* event: ready (sent once on connect)
|
|
14
|
+
* data: {"userId":"<uid>"}
|
|
15
|
+
*
|
|
16
|
+
* event: notification (sent for each published DTO)
|
|
17
|
+
* data: <NotificationDTO JSON>
|
|
18
|
+
*
|
|
19
|
+
* : heartbeat\n\n (every 25 s, no `event:` line)
|
|
20
|
+
*
|
|
21
|
+
* The `ready` frame is the client's signal that the server-side
|
|
22
|
+
* subscription is wired up. Until it arrives, the client must NOT
|
|
23
|
+
* stop polling — there's still a window where the open() handshake
|
|
24
|
+
* has fired but the bus subscription hasn't been attached yet.
|
|
25
|
+
*/
|
|
26
|
+
import { subscribeUserNotifications } from './notification-bus.js';
|
|
27
|
+
import { encodeSseEvent } from './sse.js';
|
|
28
|
+
export { encodeSseEvent };
|
|
29
|
+
const encoder = new TextEncoder();
|
|
30
|
+
/**
|
|
31
|
+
* Heartbeat interval. 25 s sits safely under the most aggressive
|
|
32
|
+
* proxy/idle timeouts we've encountered in production (Cloudflare 30 s,
|
|
33
|
+
* Vercel Serverless 30 s, most reverse-proxies 60 s). Identical to the
|
|
34
|
+
* preview stream's value so both channels behave the same way under
|
|
35
|
+
* the same proxy fleet.
|
|
36
|
+
*/
|
|
37
|
+
export const NOTIFICATION_SSE_HEARTBEAT_MS = 25_000;
|
|
38
|
+
/**
|
|
39
|
+
* Maximum stream lifetime in ms. After this, the server closes the
|
|
40
|
+
* stream cleanly so `EventSource` reconnects — important because some
|
|
41
|
+
* platforms (Vercel Fluid Compute, Cloudflare Workers) cap any single
|
|
42
|
+
* response at a few minutes regardless of activity.
|
|
43
|
+
*/
|
|
44
|
+
export const NOTIFICATION_SSE_MAX_LIFETIME_MS = 4 * 60 * 1000;
|
|
45
|
+
/**
|
|
46
|
+
* Build a `ReadableStream<Uint8Array>` of notification SSE events for
|
|
47
|
+
* the given userId. See the file header for the wire format.
|
|
48
|
+
*
|
|
49
|
+
* The stream:
|
|
50
|
+
*
|
|
51
|
+
* 1. Emits the `ready` frame.
|
|
52
|
+
* 2. Subscribes to the notification bus for `userId`.
|
|
53
|
+
* 3. Forwards every `publishNotification(...)` to the client.
|
|
54
|
+
* 4. Sends heartbeats every `heartbeatMs`.
|
|
55
|
+
* 5. Closes cleanly after `maxLifetimeMs`, letting `EventSource`
|
|
56
|
+
* auto-reconnect (the browser does this for us at the HTTP/2
|
|
57
|
+
* stream layer).
|
|
58
|
+
*
|
|
59
|
+
* On `cancel()` (client disconnect or `controller.close()`), every
|
|
60
|
+
* timer is cleared and the bus subscription is dropped — no leaked
|
|
61
|
+
* listener after a hang-up.
|
|
62
|
+
*/
|
|
63
|
+
export function createNotificationStream(userId, opts = {}) {
|
|
64
|
+
if (!userId) {
|
|
65
|
+
throw new Error('createNotificationStream requires a non-empty userId');
|
|
66
|
+
}
|
|
67
|
+
const heartbeatMs = opts.heartbeatMs ?? NOTIFICATION_SSE_HEARTBEAT_MS;
|
|
68
|
+
const maxLifetimeMs = opts.maxLifetimeMs ?? NOTIFICATION_SSE_MAX_LIFETIME_MS;
|
|
69
|
+
let heartbeat;
|
|
70
|
+
let lifetime;
|
|
71
|
+
let unsubscribe;
|
|
72
|
+
let torn = false;
|
|
73
|
+
// Single teardown path shared by:
|
|
74
|
+
// - the lifetime timer (server-initiated close before the
|
|
75
|
+
// platform's max-duration cap fires), and
|
|
76
|
+
// - the consumer's reader.cancel() (client disconnect).
|
|
77
|
+
// `cancel()` is NOT invoked when the controller closes itself —
|
|
78
|
+
// only on consumer-initiated cancellation. Without this shared
|
|
79
|
+
// helper we'd leak the bus subscription whenever the lifetime
|
|
80
|
+
// timer fires.
|
|
81
|
+
const teardown = () => {
|
|
82
|
+
if (torn)
|
|
83
|
+
return;
|
|
84
|
+
torn = true;
|
|
85
|
+
if (heartbeat)
|
|
86
|
+
clearInterval(heartbeat);
|
|
87
|
+
if (lifetime)
|
|
88
|
+
clearTimeout(lifetime);
|
|
89
|
+
if (unsubscribe)
|
|
90
|
+
unsubscribe();
|
|
91
|
+
};
|
|
92
|
+
return new ReadableStream({
|
|
93
|
+
start(controller) {
|
|
94
|
+
const enqueue = (chunk) => {
|
|
95
|
+
try {
|
|
96
|
+
controller.enqueue(encoder.encode(chunk));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Controller can be closed mid-emit if the client disconnected
|
|
100
|
+
// between the publishNotification call and the enqueue.
|
|
101
|
+
// Swallow — teardown() takes care of the rest.
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
enqueue(encodeSseEvent({ userId }, 'ready'));
|
|
105
|
+
unsubscribe = subscribeUserNotifications(userId, (notification) => {
|
|
106
|
+
enqueue(encodeSseEvent(notification, 'notification'));
|
|
107
|
+
});
|
|
108
|
+
heartbeat = setInterval(() => {
|
|
109
|
+
enqueue(`: heartbeat\n\n`);
|
|
110
|
+
}, heartbeatMs);
|
|
111
|
+
lifetime = setTimeout(() => {
|
|
112
|
+
// Drop the bus subscription BEFORE closing the controller —
|
|
113
|
+
// otherwise the listener would still be attached for the
|
|
114
|
+
// microtask between close() and the next event loop tick.
|
|
115
|
+
teardown();
|
|
116
|
+
try {
|
|
117
|
+
controller.close();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
/* already closed */
|
|
121
|
+
}
|
|
122
|
+
}, maxLifetimeMs);
|
|
123
|
+
},
|
|
124
|
+
cancel() {
|
|
125
|
+
teardown();
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Standard SSE response headers. Identical to the preview stream's
|
|
131
|
+
* header set — same proxy fleet, same X-Accel-Buffering disable, same
|
|
132
|
+
* cache-control directives.
|
|
133
|
+
*/
|
|
134
|
+
export const NOTIFICATION_SSE_HEADERS = {
|
|
135
|
+
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
136
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
137
|
+
Connection: 'keep-alive',
|
|
138
|
+
// Discourage proxies/CDNs from buffering — without this nginx will
|
|
139
|
+
// hold events back until 4 KB of payload accumulates.
|
|
140
|
+
'X-Accel-Buffering': 'no',
|
|
141
|
+
};
|
|
142
|
+
export function createNotificationStreamResponse(userId, opts) {
|
|
143
|
+
return new Response(createNotificationStream(userId, opts), {
|
|
144
|
+
status: 200,
|
|
145
|
+
headers: NOTIFICATION_SSE_HEADERS,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=notification-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-stream.js","sourceRoot":"","sources":["../../src/realtime/notification-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -197,13 +197,5 @@ export declare function markAllRead(db: NotificationsDB, userId: string): Promis
|
|
|
197
197
|
* a single-line preview.
|
|
198
198
|
*/
|
|
199
199
|
export declare function excerptForNotification(body: string, max?: number): string;
|
|
200
|
-
/**
|
|
201
|
-
* Extract `@<userId>` mentions from a comment body. We intentionally
|
|
202
|
-
* match the *user id* form here (`@user_abc123`) — a slice-7 follow-up
|
|
203
|
-
* can wire up `@username` resolution against the User table.
|
|
204
|
-
*
|
|
205
|
-
* Returns a deduplicated array of mentioned user ids; the caller filters
|
|
206
|
-
* out the actor before issuing notifications.
|
|
207
|
-
*/
|
|
208
200
|
export declare function extractMentionedUserIds(body: string): string[];
|
|
209
201
|
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/realtime/notifications.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/realtime/notifications.ts"],"names":[],"mappings":"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"}
|
|
@@ -189,20 +189,44 @@ export function excerptForNotification(body, max = 120) {
|
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
191
|
* Extract `@<userId>` mentions from a comment body. We intentionally
|
|
192
|
-
* match the *user id* form here (`@user_abc123`) —
|
|
193
|
-
*
|
|
192
|
+
* match the *user id* form here (`@user_abc123`) — `@username`
|
|
193
|
+
* resolution against the User table is a separate slice that can
|
|
194
|
+
* land later on top of this same wire format.
|
|
194
195
|
*
|
|
195
196
|
* Returns a deduplicated array of mentioned user ids; the caller filters
|
|
196
197
|
* out the actor before issuing notifications.
|
|
198
|
+
*
|
|
199
|
+
* Lock-step contract with the admin UI
|
|
200
|
+
* ────────────────────────────────────
|
|
201
|
+
* The mention picker in `cms-admin/src/lib/mention-picker.ts`
|
|
202
|
+
* (`detectMentionContext`) must agree with this regex on what counts
|
|
203
|
+
* as a mention, or the picker's intuition ("this isn't a mention so
|
|
204
|
+
* I won't show a dropdown") will diverge from the server's
|
|
205
|
+
* notification routing ("this IS a mention, sending one"). Two
|
|
206
|
+
* properties must hold:
|
|
207
|
+
*
|
|
208
|
+
* 1. The `@` is at the start of the body OR preceded by whitespace
|
|
209
|
+
* / newline (so `foo@bar` is an email, NOT a mention — picker
|
|
210
|
+
* review on PR #159).
|
|
211
|
+
* 2. The token charset is `[a-zA-Z0-9_-]` with a 3–64 char length.
|
|
212
|
+
* The picker enforces the upper bound; the lower bound exists
|
|
213
|
+
* to keep accidental `@` typos (`@a`, `@an`) from generating
|
|
214
|
+
* notifications.
|
|
197
215
|
*/
|
|
216
|
+
const MENTION_REGEX = /(^|[\s\n\r\t])@([a-zA-Z0-9_-]{3,64})\b/g;
|
|
198
217
|
export function extractMentionedUserIds(body) {
|
|
199
218
|
if (typeof body !== 'string' || body.length === 0)
|
|
200
219
|
return [];
|
|
201
220
|
const seen = new Set();
|
|
202
|
-
|
|
221
|
+
// `lastIndex` rewinding — we use a capturing group for the
|
|
222
|
+
// boundary which advances past it, but the next `@` could
|
|
223
|
+
// immediately follow. The default global regex advance handles
|
|
224
|
+
// this correctly because `lastIndex` lands AFTER the full match,
|
|
225
|
+
// and the boundary character is consumed exactly once.
|
|
203
226
|
let match;
|
|
227
|
+
const re = new RegExp(MENTION_REGEX.source, 'g');
|
|
204
228
|
while ((match = re.exec(body)) !== null) {
|
|
205
|
-
seen.add(match[
|
|
229
|
+
seen.add(match[2]);
|
|
206
230
|
}
|
|
207
231
|
return Array.from(seen);
|
|
208
232
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../src/realtime/notifications.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../src/realtime/notifications.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal SSE wire-format helpers shared by every Server-Sent Events
|
|
3
|
+
* stream in `cms-core` (preview updates and per-user notifications,
|
|
4
|
+
* today). Kept dependency-free so any future stream can `import` it
|
|
5
|
+
* without dragging in module state from sibling channels.
|
|
6
|
+
*
|
|
7
|
+
* A single source of truth here means a bug in the encoder is fixed
|
|
8
|
+
* once, not in N parallel copies.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Encode a single SSE message. `event` is optional; when omitted the
|
|
12
|
+
* default `message` event name is used (per the SSE spec). Multi-line
|
|
13
|
+
* payloads are split across multiple `data:` lines so the receiving
|
|
14
|
+
* `EventSource` reassembles them.
|
|
15
|
+
*
|
|
16
|
+
* The trailing blank line is the SSE event delimiter — DO NOT drop
|
|
17
|
+
* it. Browsers will hold the event in their buffer until they see it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function encodeSseEvent(data: unknown, event?: string): string;
|
|
20
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/realtime/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAUpE"}
|