opinionated-machine 6.14.0 → 6.15.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/README.md +196 -0
- package/dist/lib/sse/AbstractSSEController.js +4 -3
- package/dist/lib/sse/AbstractSSEController.js.map +1 -1
- package/dist/lib/sse/index.d.ts +2 -1
- package/dist/lib/sse/index.js +2 -0
- package/dist/lib/sse/index.js.map +1 -1
- package/dist/lib/sse/rooms/SSERoomBroadcaster.d.ts +20 -4
- package/dist/lib/sse/rooms/SSERoomBroadcaster.js +44 -12
- package/dist/lib/sse/rooms/SSERoomBroadcaster.js.map +1 -1
- package/dist/lib/sse/rooms/SSERoomManager.d.ts +5 -2
- package/dist/lib/sse/rooms/SSERoomManager.js +8 -5
- package/dist/lib/sse/rooms/SSERoomManager.js.map +1 -1
- package/dist/lib/sse/rooms/adapters/InMemoryAdapter.d.ts +1 -1
- package/dist/lib/sse/rooms/adapters/InMemoryAdapter.js +1 -1
- package/dist/lib/sse/rooms/adapters/InMemoryAdapter.js.map +1 -1
- package/dist/lib/sse/rooms/index.d.ts +1 -1
- package/dist/lib/sse/rooms/types.d.ts +34 -2
- package/dist/lib/sse/subscriptions/SSESubscriptionManager.d.ts +47 -0
- package/dist/lib/sse/subscriptions/SSESubscriptionManager.js +298 -0
- package/dist/lib/sse/subscriptions/SSESubscriptionManager.js.map +1 -0
- package/dist/lib/sse/subscriptions/defineEventMetadata.d.ts +50 -0
- package/dist/lib/sse/subscriptions/defineEventMetadata.js +44 -0
- package/dist/lib/sse/subscriptions/defineEventMetadata.js.map +1 -0
- package/dist/lib/sse/subscriptions/index.d.ts +3 -0
- package/dist/lib/sse/subscriptions/index.js +3 -0
- package/dist/lib/sse/subscriptions/index.js.map +1 -0
- package/dist/lib/sse/subscriptions/types.d.ts +141 -0
- package/dist/lib/sse/subscriptions/types.js +2 -0
- package/dist/lib/sse/subscriptions/types.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,17 @@ Very opinionated DI framework for fastify, built on top of awilix
|
|
|
58
58
|
- [Room Query Methods](#room-query-methods)
|
|
59
59
|
- [Auto-Leave on Disconnect](#auto-leave-on-disconnect)
|
|
60
60
|
- [Multi-Node Deployments with Redis](#multi-node-deployments-with-redis)
|
|
61
|
+
- [SSE Subscriptions](#sse-subscriptions)
|
|
62
|
+
- [Defining Event Metadata](#defining-event-metadata)
|
|
63
|
+
- [Defining Resolvers](#defining-resolvers)
|
|
64
|
+
- [Configuring the Manager](#configuring-the-manager)
|
|
65
|
+
- [Integrating with a Controller](#integrating-with-a-controller)
|
|
66
|
+
- [Publishing Events](#publishing-events)
|
|
67
|
+
- [Refreshing Preferences Mid-Connection](#refreshing-preferences-mid-connection)
|
|
68
|
+
- [Pipeline Semantics](#pipeline-semantics)
|
|
69
|
+
- [Multi-Node Support](#multi-node-support)
|
|
70
|
+
- [Data Loading with layered-loader](#data-loading-with-layered-loader)
|
|
71
|
+
- [Testing](#testing)
|
|
61
72
|
- [SSE Test Utilities](#sse-test-utilities)
|
|
62
73
|
- [Quick Reference](#quick-reference)
|
|
63
74
|
- [Inject vs HTTP Comparison](#inject-vs-http-comparison)
|
|
@@ -1803,6 +1814,191 @@ The Redis adapter uses Pub/Sub for cross-node message propagation. When you call
|
|
|
1803
1814
|
|
|
1804
1815
|
See the [@opinionated-machine/sse-rooms-redis](./packages/sse-rooms-redis/README.md) package for detailed documentation on Redis adapter configuration and usage.
|
|
1805
1816
|
|
|
1817
|
+
### SSE Subscriptions
|
|
1818
|
+
|
|
1819
|
+
SSE Subscriptions add user-centered event filtering on top of SSE Rooms. Users connect once to a universal stream, and a **resolver pipeline** determines which events reach them based on membership, preferences, and arbitrary business rules.
|
|
1820
|
+
|
|
1821
|
+
#### Defining Event Metadata
|
|
1822
|
+
|
|
1823
|
+
Define a discriminated union describing all event scopes, then create type-safe guards with `defineEventMetadata()`:
|
|
1824
|
+
|
|
1825
|
+
```typescript
|
|
1826
|
+
import { defineEventMetadata } from 'opinionated-machine'
|
|
1827
|
+
|
|
1828
|
+
type EventMetadata =
|
|
1829
|
+
| { scope: 'project'; projectId: string }
|
|
1830
|
+
| { scope: 'team'; teamId: string }
|
|
1831
|
+
| { scope: 'global' }
|
|
1832
|
+
|
|
1833
|
+
const meta = defineEventMetadata<EventMetadata>()('scope', ['project', 'team', 'global'])
|
|
1834
|
+
|
|
1835
|
+
// In resolvers, guards narrow the type:
|
|
1836
|
+
if (meta.project(event.metadata)) {
|
|
1837
|
+
event.metadata.projectId // TypeScript knows this is string
|
|
1838
|
+
}
|
|
1839
|
+
```
|
|
1840
|
+
|
|
1841
|
+
#### Defining Resolvers
|
|
1842
|
+
|
|
1843
|
+
Resolvers are stateless filters evaluated in pipeline order. Each resolver can `allow`, `deny`, or `defer`:
|
|
1844
|
+
|
|
1845
|
+
```typescript
|
|
1846
|
+
import type { SubscriptionResolver, SubscriptionContext, FilterVerdict } from 'opinionated-machine'
|
|
1847
|
+
|
|
1848
|
+
class ProjectMembershipResolver {
|
|
1849
|
+
readonly name = 'project-membership'
|
|
1850
|
+
|
|
1851
|
+
async onConnect(ctx: SubscriptionContext<UserCtx>) {
|
|
1852
|
+
const memberships = await this.membershipLoader.get(ctx.userContext.userId)
|
|
1853
|
+
const projectIds = new Set(memberships.map(m => m.projectId))
|
|
1854
|
+
return {
|
|
1855
|
+
userContext: { ...ctx.userContext, projectIds },
|
|
1856
|
+
rooms: Array.from(projectIds).map(id => `project:${id}`),
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
evaluate(ctx: SubscriptionContext<UserCtx>, event: IncomingEvent<EventMetadata>): FilterVerdict {
|
|
1861
|
+
if (meta.project(event.metadata)) {
|
|
1862
|
+
return ctx.userContext.projectIds.has(event.metadata.projectId)
|
|
1863
|
+
? { action: 'allow' }
|
|
1864
|
+
: { action: 'deny', reason: 'not a project member' }
|
|
1865
|
+
}
|
|
1866
|
+
return { action: 'defer' }
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
async refresh(ctx: SubscriptionContext<UserCtx>) {
|
|
1870
|
+
// Re-fetch memberships on demand
|
|
1871
|
+
const memberships = await this.membershipLoader.get(ctx.userContext.userId)
|
|
1872
|
+
const projectIds = new Set(memberships.map(m => m.projectId))
|
|
1873
|
+
return {
|
|
1874
|
+
userContext: { ...ctx.userContext, projectIds },
|
|
1875
|
+
rooms: Array.from(projectIds).map(id => `project:${id}`),
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
```
|
|
1880
|
+
|
|
1881
|
+
#### Configuring the Manager
|
|
1882
|
+
|
|
1883
|
+
```typescript
|
|
1884
|
+
import { SSESubscriptionManager } from 'opinionated-machine'
|
|
1885
|
+
|
|
1886
|
+
const subscriptionManager = new SSESubscriptionManager<UserCtx, EventMetadata>(
|
|
1887
|
+
{
|
|
1888
|
+
resolveUserContext: async (request) => ({
|
|
1889
|
+
userId: request.user.id,
|
|
1890
|
+
projectIds: new Set(),
|
|
1891
|
+
mutedEventTypes: new Set(),
|
|
1892
|
+
}),
|
|
1893
|
+
resolvers: [
|
|
1894
|
+
new ProjectMembershipResolver(membershipLoader),
|
|
1895
|
+
new MutePreferencesResolver(prefsLoader),
|
|
1896
|
+
],
|
|
1897
|
+
defaultPolicy: 'deny',
|
|
1898
|
+
resolveUserId: (ctx) => ctx.userId,
|
|
1899
|
+
},
|
|
1900
|
+
{ sseRoomManager, sseRoomBroadcaster },
|
|
1901
|
+
)
|
|
1902
|
+
```
|
|
1903
|
+
|
|
1904
|
+
#### Integrating with a Controller
|
|
1905
|
+
|
|
1906
|
+
Wire `handleConnect` and `handleDisconnect` into the SSE session lifecycle:
|
|
1907
|
+
|
|
1908
|
+
```typescript
|
|
1909
|
+
class NotificationController extends AbstractSSEController<Contracts> {
|
|
1910
|
+
private handleStream = buildHandler(contract, {
|
|
1911
|
+
sse: (request, sse) => {
|
|
1912
|
+
const session = sse.start('keepAlive')
|
|
1913
|
+
this.subscriptionManager.handleConnect(session).catch(() => {
|
|
1914
|
+
// Handle connection setup failure (e.g., resolver threw)
|
|
1915
|
+
})
|
|
1916
|
+
},
|
|
1917
|
+
}, {
|
|
1918
|
+
onClose: (session) => {
|
|
1919
|
+
this.subscriptionManager.handleDisconnect(session)
|
|
1920
|
+
},
|
|
1921
|
+
})
|
|
1922
|
+
}
|
|
1923
|
+
```
|
|
1924
|
+
|
|
1925
|
+
#### Publishing Events
|
|
1926
|
+
|
|
1927
|
+
```typescript
|
|
1928
|
+
const result = await subscriptionManager.publish({
|
|
1929
|
+
eventName: 'announcement',
|
|
1930
|
+
data: { message: 'New feature released!' },
|
|
1931
|
+
targetRooms: ['project:123'],
|
|
1932
|
+
metadata: { scope: 'project', projectId: '123' },
|
|
1933
|
+
})
|
|
1934
|
+
// result: { delivered: 5, filtered: 2 }
|
|
1935
|
+
```
|
|
1936
|
+
|
|
1937
|
+
`targetRooms` controls routing:
|
|
1938
|
+
- **Specific rooms** (`['project:123']`) — broadcast to those rooms, filter via resolver pipeline
|
|
1939
|
+
- **`undefined`** (omitted) — broadcast to all rooms of all managed connections
|
|
1940
|
+
- **Empty array** (`[]`) — no-op, returns `{ delivered: 0, filtered: 0 }`
|
|
1941
|
+
|
|
1942
|
+
#### Refreshing Preferences Mid-Connection
|
|
1943
|
+
|
|
1944
|
+
When a user updates preferences (e.g., mutes an event type), refresh their active connections:
|
|
1945
|
+
|
|
1946
|
+
```typescript
|
|
1947
|
+
// In your REST endpoint handler:
|
|
1948
|
+
await prefsLoader.invalidateCacheFor(userId)
|
|
1949
|
+
await subscriptionManager.refreshUser(userId)
|
|
1950
|
+
```
|
|
1951
|
+
|
|
1952
|
+
The manager diffs rooms and joins/leaves as needed — no reconnection required.
|
|
1953
|
+
|
|
1954
|
+
#### Pipeline Semantics
|
|
1955
|
+
|
|
1956
|
+
- Resolvers are evaluated in array order
|
|
1957
|
+
- First `deny` short-circuits — event is not delivered
|
|
1958
|
+
- `allow` does not short-circuit — subsequent resolvers can still deny
|
|
1959
|
+
- If all resolvers return `defer`, `defaultPolicy` applies (default: `deny`)
|
|
1960
|
+
- Resolver `evaluate()` errors are treated as `deny`
|
|
1961
|
+
- Resolver `refresh()` errors are caught per-resolver — the failed resolver keeps its previous state while remaining resolvers continue refreshing
|
|
1962
|
+
- Later resolvers in the array receive the accumulated `userContext` from earlier resolvers — use spread (`{ ...ctx.userContext, ...newFields }`) to preserve prior resolver data
|
|
1963
|
+
- `defaultPolicy` defaults to `'deny'` when not specified
|
|
1964
|
+
|
|
1965
|
+
#### Multi-Node Support
|
|
1966
|
+
|
|
1967
|
+
- Metadata flows through the adapter chain (Redis pub/sub) alongside the SSE message
|
|
1968
|
+
- Resolver pipeline runs locally on each node for its own connections
|
|
1969
|
+
- Wire format is a single v1 schema with optional `meta` — older nodes simply have no metadata
|
|
1970
|
+
- Use `layered-loader` for distributed cache invalidation across nodes
|
|
1971
|
+
|
|
1972
|
+
#### Data Loading with layered-loader
|
|
1973
|
+
|
|
1974
|
+
`layered-loader` is recommended (not required) for resolver data loading. It provides in-memory → Redis → DB caching with TTL, refresh-ahead, and distributed invalidation:
|
|
1975
|
+
|
|
1976
|
+
```typescript
|
|
1977
|
+
import { Loader } from 'layered-loader'
|
|
1978
|
+
|
|
1979
|
+
const membershipLoader = new Loader<ProjectMembership[]>({
|
|
1980
|
+
inMemoryCache: { cacheType: 'lru-map', ttlInMsecs: 120_000, maxItems: 500 },
|
|
1981
|
+
asyncCache: new RedisCache(redis, { json: true, ttlInMsecs: 900_000 }),
|
|
1982
|
+
dataSources: [membershipDataSource],
|
|
1983
|
+
})
|
|
1984
|
+
```
|
|
1985
|
+
|
|
1986
|
+
#### Testing
|
|
1987
|
+
|
|
1988
|
+
Create mock resolvers for unit tests:
|
|
1989
|
+
|
|
1990
|
+
```typescript
|
|
1991
|
+
const mockResolver = {
|
|
1992
|
+
name: 'mock',
|
|
1993
|
+
evaluate: vi.fn().mockReturnValue({ action: 'allow' }),
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
const manager = new SSESubscriptionManager(
|
|
1997
|
+
{ resolveUserContext: async () => mockContext, resolvers: [mockResolver] },
|
|
1998
|
+
{ sseRoomManager, sseRoomBroadcaster },
|
|
1999
|
+
)
|
|
2000
|
+
```
|
|
2001
|
+
|
|
1806
2002
|
### SSE Test Utilities
|
|
1807
2003
|
|
|
1808
2004
|
The library provides utilities for testing SSE endpoints.
|
|
@@ -393,9 +393,9 @@ export class AbstractSSEController {
|
|
|
393
393
|
* })
|
|
394
394
|
* ```
|
|
395
395
|
*/
|
|
396
|
-
broadcastToRoom(room, eventName, data, options) {
|
|
396
|
+
async broadcastToRoom(room, eventName, data, options) {
|
|
397
397
|
if (!this._roomBroadcaster) {
|
|
398
|
-
return
|
|
398
|
+
return 0;
|
|
399
399
|
}
|
|
400
400
|
const message = {
|
|
401
401
|
event: eventName,
|
|
@@ -403,7 +403,8 @@ export class AbstractSSEController {
|
|
|
403
403
|
id: options?.id ?? randomUUID(),
|
|
404
404
|
retry: options?.retry,
|
|
405
405
|
};
|
|
406
|
-
|
|
406
|
+
const { delivered } = await this._roomBroadcaster.broadcastMessage(room, message, options);
|
|
407
|
+
return delivered;
|
|
407
408
|
}
|
|
408
409
|
/**
|
|
409
410
|
* Join a connection to one or more rooms.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractSSEController.js","sourceRoot":"","sources":["../../../lib/sse/AbstractSSEController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAOxC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAOnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAmBlD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAkBlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,OAAgB,qBAAqB;IAGzC,gDAAgD;IACtC,WAAW,GAA4B,IAAI,GAAG,EAAE,CAAA;IAE1D,yCAAyC;IACxB,cAAc,CAAgB;IAE/C,0DAA0D;IACzC,YAAY,CAAiB;IAE9C,kEAAkE;IACjD,gBAAgB,CAAqB;IAEtD;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,aAAqB,EAAE,SAA+B;QAChE,IAAI,SAAS,EAAE,mBAAmB,EAAE,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,aAAa,EAAE,CAAA;QAC3C,CAAC;QAED,IAAI,SAAS,EAAE,eAAe,EAAE,CAAC;YAC/B,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC,eAAe,CAAA;YACjD,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC,eAAe,CAAC,WAAW,CAAA;YACzD,SAAS,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAA;QAChF,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,IAAW,aAAa;QACtB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,wFAAwF;gBACtF,gDAAgD,CACnD,CAAA;QACH,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAA;IAC5B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,IAAW,eAAe;QACxB,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,2FAA2F,CAC5F,CAAA;QACH,CAAC;QACD,OAAO,IAAI,CAAC,gBAAgB,CAAA;IAC9B,CAAC;IA0BD;;;;;;;;;;;;;;;OAeG;IACK,KAAK,CAAC,SAAS,CAAI,YAAoB,EAAE,OAAsB;QACrE,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACrD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,KAAK,CAAA;QACd,CAAC;QAED,kDAAkD;QAClD,IAAI,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,YAAY,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACrD,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gBAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBACpB,MAAM,IAAI,aAAa,CAAC;wBACtB,OAAO,EAAE,0CAA0C,OAAO,CAAC,KAAK,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;wBAC5F,SAAS,EAAE,4BAA4B;qBACxC,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,UAAU,CAAC,KAAiB,CAAA;YAC1C,iFAAiF;YACjF,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;gBACnB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC,CAAA;YACF,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,MAAM,CAAC;YACP,uFAAuF;YACvF,6EAA6E;YAC7E,gEAAgE;YAChE,IAAI,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAA;YACvC,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACI,aAAa,CAAI,YAAoB,EAAE,OAAsB;QAClE,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IAC9C,CAAC;IAED;;;;OAIG;IACH,IAAW,oBAAoB;QAC7B,OAAO,IAAI,CAAC,YAAY,CAAA;IAC1B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACI,iBAAiB,CACtB,YAAoB,EACpB,OAKC;QAED,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IAC9C,CAAC;IAED;;;;;OAKG;IACO,KAAK,CAAC,SAAS,CAAI,OAAsB;QACjD,IAAI,IAAI,GAAG,CAAC,CAAA;QACZ,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAA;QACzD,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;YAC/B,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC;gBACtC,IAAI,EAAE,CAAA;YACR,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;;OAMG;IACO,KAAK,CAAC,WAAW,CACzB,OAAsB,EACtB,SAA8C;QAE9C,IAAI,IAAI,GAAG,CAAC,CAAA;QACZ,KAAK,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAChD,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;gBACjE,IAAI,EAAE,CAAA;YACR,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;OAEG;IACO,cAAc;QACtB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAA;IAC9C,CAAC;IAED;;OAEG;IACO,kBAAkB;QAC1B,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;IAC9B,CAAC;IAED;;;;;;;;;;;OAWG;IACI,eAAe,CAAC,YAAoB;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACrD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,UAAU,CAAC,KAAiB,CAAA;YAC1C,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;QACrC,CAAC;QAED,gEAAgE;QAChE,IAAI,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAA;QACvC,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;OAGG;IACI,mBAAmB;QACxB,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAA;QACzD,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,kBAAkB,CAAC,UAAsB;QAC9C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;QAE/C,IAAI,CAAC,uBAAuB,EAAE,CAAC,UAAU,CAAC,CAAA;QAC1C,qEAAqE;QACrE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;IAChD,CAAC;IAED;;;;;;OAMG;IACI,oBAAoB,CAAC,YAAoB;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACrD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,kEAAkE;YAClE,OAAM;QACR,CAAC;QACD,IAAI,CAAC,kBAAkB,EAAE,CAAC,UAAU,CAAC,CAAA;QACrC,8BAA8B;QAC9B,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;QAEnD,qCAAqC;QACrC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAA;QACzC,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAA;QAEtD,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IACvC,CAAC;IAED,+EAA+E;IAC/E,kBAAkB;IAClB,+EAA+E;IAE/E;;;;;OAKG;IACH,IAAc,WAAW;QACvB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,qFAAqF,CACtF,CAAA;QACH,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAA;IAC1B,CAAC;IAED;;OAEG;IACH,IAAc,YAAY;QACxB,OAAO,IAAI,CAAC,YAAY,KAAK,SAAS,CAAA;IACxC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACO,eAAe,
|
|
1
|
+
{"version":3,"file":"AbstractSSEController.js","sourceRoot":"","sources":["../../../lib/sse/AbstractSSEController.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/sse/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type { AllContractEventNames, AllContractEvents, AnySSEContractDefinition
|
|
|
2
2
|
export { type BuildFastifySSERoutesReturnType, buildFastifyRoute, buildHandler, type FastifySSEHandlerConfig, type FastifySSEPreHandler, type FastifySSERouteOptions, type InferSSERequest, type RegisterSSERoutesOptions, type SSEContext, type SSEOnlyHandlers, type SSERouteHandler, type SSESession, } from '../routes/index.js';
|
|
3
3
|
export { AbstractSSEController, type SSEControllerConfig, type SSEEventSender, type SSELogger, type SSEMessage, } from './AbstractSSEController.js';
|
|
4
4
|
export { defineEvent, type SSEEventDefinition } from './defineEvent.js';
|
|
5
|
-
export { defineRoom, InMemoryAdapter, type RoomBroadcastOptions, type RoomNameResolver, type SSERoomAdapter, SSERoomBroadcaster, SSERoomManager, type SSERoomManagerConfig, type SSERoomMessageHandler, type SSERoomOperations, } from './rooms/index.js';
|
|
5
|
+
export { defineRoom, InMemoryAdapter, type PreDeliveryFilter, type RoomBroadcastOptions, type RoomNameResolver, type SSERoomAdapter, SSERoomBroadcaster, SSERoomManager, type SSERoomManagerConfig, type SSERoomMessageHandler, type SSERoomOperations, } from './rooms/index.js';
|
|
6
6
|
export { type SSESessionEvent, SSESessionSpy } from './SSESessionSpy.js';
|
|
7
7
|
export { type ParsedSSEEvent, type ParseSSEBufferResult, parseSSEBuffer, parseSSEEvents, } from './sseParser.js';
|
|
8
|
+
export { defineEventMetadata, type ExtractMetadata, type FilterVerdict, type IncomingEvent, type MetadataGuard, type MetadataGuards, type PublishResult, type ResolverResult, SSESubscriptionManager, type SSESubscriptionManagerConfig, type SubscriptionContext, type SubscriptionPolicy, type SubscriptionResolver, } from './subscriptions/index.js';
|
package/dist/lib/sse/index.js
CHANGED
|
@@ -6,4 +6,6 @@ export { defineEvent } from './defineEvent.js';
|
|
|
6
6
|
export { defineRoom, InMemoryAdapter, SSERoomBroadcaster, SSERoomManager, } from './rooms/index.js';
|
|
7
7
|
export { SSESessionSpy } from './SSESessionSpy.js';
|
|
8
8
|
export { parseSSEBuffer, parseSSEEvents, } from './sseParser.js';
|
|
9
|
+
// SSE Subscriptions
|
|
10
|
+
export { defineEventMetadata, SSESubscriptionManager, } from './subscriptions/index.js';
|
|
9
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/sse/index.ts"],"names":[],"mappings":"AAUA,2CAA2C;AAC3C,OAAO,EAEL,iBAAiB,EACjB,YAAY,GAUb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,qBAAqB,GAKtB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,WAAW,EAA2B,MAAM,kBAAkB,CAAA;AACvE,mCAAmC;AACnC,OAAO,EACL,UAAU,EACV,eAAe,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/sse/index.ts"],"names":[],"mappings":"AAUA,2CAA2C;AAC3C,OAAO,EAEL,iBAAiB,EACjB,YAAY,GAUb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,qBAAqB,GAKtB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,WAAW,EAA2B,MAAM,kBAAkB,CAAA;AACvE,mCAAmC;AACnC,OAAO,EACL,UAAU,EACV,eAAe,EAKf,kBAAkB,EAClB,cAAc,GAIf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAwB,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACxE,OAAO,EAGL,cAAc,EACd,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,oBAAoB;AACpB,OAAO,EACL,mBAAmB,EAQnB,sBAAsB,GAKvB,MAAM,0BAA0B,CAAA"}
|
|
@@ -2,7 +2,7 @@ import type { z } from 'zod';
|
|
|
2
2
|
import type { SSEEventDefinition } from '../defineEvent.js';
|
|
3
3
|
import type { SSEMessage } from '../sseTypes.js';
|
|
4
4
|
import type { SSERoomManager } from './SSERoomManager.js';
|
|
5
|
-
import type { RoomBroadcastOptions } from './types.js';
|
|
5
|
+
import type { BroadcastResult, PreDeliveryFilter, RoomBroadcastOptions } from './types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Shared, non-generic room broadcaster that can be registered once in DI
|
|
8
8
|
* and used by multiple controllers and domain services.
|
|
@@ -31,9 +31,18 @@ export declare class SSERoomBroadcaster {
|
|
|
31
31
|
private readonly _roomManager;
|
|
32
32
|
private readonly senders;
|
|
33
33
|
private readonly dedupCache;
|
|
34
|
+
private preDeliveryFilter?;
|
|
34
35
|
constructor(deps: {
|
|
35
36
|
sseRoomManager: SSERoomManager;
|
|
36
37
|
});
|
|
38
|
+
/**
|
|
39
|
+
* Set a pre-delivery filter that runs before sending to each connection.
|
|
40
|
+
* Used by SSESubscriptionManager for resolver pipeline evaluation.
|
|
41
|
+
* Only one filter can be active at a time — calling this method twice throws,
|
|
42
|
+
* to prevent silently replacing an already-installed filter (which is almost
|
|
43
|
+
* always a wiring bug, e.g. registering two SSESubscriptionManagers).
|
|
44
|
+
*/
|
|
45
|
+
setPreDeliveryFilter(filter: PreDeliveryFilter): void;
|
|
37
46
|
/**
|
|
38
47
|
* Public getter for the underlying room manager.
|
|
39
48
|
* Used by the route builder for `session.rooms`.
|
|
@@ -54,11 +63,13 @@ export declare class SSERoomBroadcaster {
|
|
|
54
63
|
* @param event - Event definition created by `defineEvent()`
|
|
55
64
|
* @param data - Event data (must match the schema from the event definition)
|
|
56
65
|
* @param options - Broadcast options (local, id, retry)
|
|
57
|
-
* @returns Number of local connections the message was
|
|
66
|
+
* @returns Number of local connections the message was successfully delivered to.
|
|
67
|
+
* For separate `delivered`/`filtered` counts, call {@link broadcastMessage}.
|
|
58
68
|
*/
|
|
59
69
|
broadcastToRoom<T extends z.ZodType>(room: string | string[], event: SSEEventDefinition<string, T>, data: z.input<T>, options?: RoomBroadcastOptions & {
|
|
60
70
|
id?: string;
|
|
61
71
|
retry?: number;
|
|
72
|
+
metadata?: Record<string, unknown>;
|
|
62
73
|
}): Promise<number>;
|
|
63
74
|
/**
|
|
64
75
|
* Lower-level broadcast API — sends a raw SSEMessage to all connections in one or more rooms.
|
|
@@ -68,9 +79,14 @@ export declare class SSERoomBroadcaster {
|
|
|
68
79
|
* @param room - Room name or array of room names
|
|
69
80
|
* @param message - The SSE message to broadcast
|
|
70
81
|
* @param options - Broadcast options (local)
|
|
71
|
-
* @returns
|
|
82
|
+
* @returns `delivered` is the number of local connections the message was sent to;
|
|
83
|
+
* `filtered` is the number of local connections skipped by the pre-delivery
|
|
84
|
+
* filter. Returning both per-call (rather than tracking on the broadcaster)
|
|
85
|
+
* avoids races between concurrent broadcasts.
|
|
72
86
|
*/
|
|
73
|
-
broadcastMessage(room: string | string[], message: SSEMessage, options?: RoomBroadcastOptions
|
|
87
|
+
broadcastMessage(room: string | string[], message: SSEMessage, options?: RoomBroadcastOptions & {
|
|
88
|
+
metadata?: Record<string, unknown>;
|
|
89
|
+
}): Promise<BroadcastResult>;
|
|
74
90
|
/**
|
|
75
91
|
* Get all connection IDs in a room.
|
|
76
92
|
*
|
|
@@ -29,13 +29,27 @@ export class SSERoomBroadcaster {
|
|
|
29
29
|
_roomManager;
|
|
30
30
|
senders = [];
|
|
31
31
|
dedupCache = new Map();
|
|
32
|
+
preDeliveryFilter;
|
|
32
33
|
constructor(deps) {
|
|
33
34
|
this._roomManager = deps.sseRoomManager;
|
|
34
35
|
// Wire up adapter message handler to forward remote broadcasts to local connections
|
|
35
|
-
this._roomManager.onRemoteMessage((room, message, _sourceNodeId) => {
|
|
36
|
-
return this.handleRemoteBroadcast(room, message);
|
|
36
|
+
this._roomManager.onRemoteMessage((room, message, _sourceNodeId, metadata) => {
|
|
37
|
+
return this.handleRemoteBroadcast(room, message, metadata);
|
|
37
38
|
});
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Set a pre-delivery filter that runs before sending to each connection.
|
|
42
|
+
* Used by SSESubscriptionManager for resolver pipeline evaluation.
|
|
43
|
+
* Only one filter can be active at a time — calling this method twice throws,
|
|
44
|
+
* to prevent silently replacing an already-installed filter (which is almost
|
|
45
|
+
* always a wiring bug, e.g. registering two SSESubscriptionManagers).
|
|
46
|
+
*/
|
|
47
|
+
setPreDeliveryFilter(filter) {
|
|
48
|
+
if (this.preDeliveryFilter) {
|
|
49
|
+
throw new Error('SSERoomBroadcaster pre-delivery filter is already set; only one filter can be active at a time');
|
|
50
|
+
}
|
|
51
|
+
this.preDeliveryFilter = filter;
|
|
52
|
+
}
|
|
39
53
|
/**
|
|
40
54
|
* Public getter for the underlying room manager.
|
|
41
55
|
* Used by the route builder for `session.rooms`.
|
|
@@ -60,16 +74,18 @@ export class SSERoomBroadcaster {
|
|
|
60
74
|
* @param event - Event definition created by `defineEvent()`
|
|
61
75
|
* @param data - Event data (must match the schema from the event definition)
|
|
62
76
|
* @param options - Broadcast options (local, id, retry)
|
|
63
|
-
* @returns Number of local connections the message was
|
|
77
|
+
* @returns Number of local connections the message was successfully delivered to.
|
|
78
|
+
* For separate `delivered`/`filtered` counts, call {@link broadcastMessage}.
|
|
64
79
|
*/
|
|
65
|
-
broadcastToRoom(room, event, data, options) {
|
|
80
|
+
async broadcastToRoom(room, event, data, options) {
|
|
66
81
|
const message = {
|
|
67
82
|
event: event.event,
|
|
68
83
|
data,
|
|
69
84
|
id: options?.id ?? randomUUID(),
|
|
70
85
|
retry: options?.retry,
|
|
71
86
|
};
|
|
72
|
-
|
|
87
|
+
const { delivered } = await this.broadcastMessage(room, message, options);
|
|
88
|
+
return delivered;
|
|
73
89
|
}
|
|
74
90
|
/**
|
|
75
91
|
* Lower-level broadcast API — sends a raw SSEMessage to all connections in one or more rooms.
|
|
@@ -79,25 +95,36 @@ export class SSERoomBroadcaster {
|
|
|
79
95
|
* @param room - Room name or array of room names
|
|
80
96
|
* @param message - The SSE message to broadcast
|
|
81
97
|
* @param options - Broadcast options (local)
|
|
82
|
-
* @returns
|
|
98
|
+
* @returns `delivered` is the number of local connections the message was sent to;
|
|
99
|
+
* `filtered` is the number of local connections skipped by the pre-delivery
|
|
100
|
+
* filter. Returning both per-call (rather than tracking on the broadcaster)
|
|
101
|
+
* avoids races between concurrent broadcasts.
|
|
83
102
|
*/
|
|
84
103
|
async broadcastMessage(room, message, options) {
|
|
85
104
|
const rooms = Array.isArray(room) ? room : [room];
|
|
86
105
|
const connectionIds = this.collectRoomConnections(rooms);
|
|
87
|
-
|
|
88
|
-
let
|
|
106
|
+
let delivered = 0;
|
|
107
|
+
let filtered = 0;
|
|
89
108
|
for (const connId of connectionIds) {
|
|
109
|
+
// Check pre-delivery filter if set
|
|
110
|
+
if (this.preDeliveryFilter) {
|
|
111
|
+
const allowed = await this.preDeliveryFilter(connId, message, options?.metadata);
|
|
112
|
+
if (!allowed) {
|
|
113
|
+
filtered++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
90
117
|
if (await this.sendToConnection(connId, message)) {
|
|
91
|
-
|
|
118
|
+
delivered++;
|
|
92
119
|
}
|
|
93
120
|
}
|
|
94
121
|
// Publish to adapter for cross-node propagation (unless local-only)
|
|
95
122
|
if (!options?.local) {
|
|
96
123
|
for (const r of rooms) {
|
|
97
|
-
await this._roomManager.publish(r, message, options);
|
|
124
|
+
await this._roomManager.publish(r, message, options, options?.metadata);
|
|
98
125
|
}
|
|
99
126
|
}
|
|
100
|
-
return
|
|
127
|
+
return { delivered, filtered };
|
|
101
128
|
}
|
|
102
129
|
/**
|
|
103
130
|
* Get all connection IDs in a room.
|
|
@@ -139,12 +166,17 @@ export class SSERoomBroadcaster {
|
|
|
139
166
|
* Handle broadcasts from other nodes (via adapter).
|
|
140
167
|
* Deduplicates messages per-connection based on message ID.
|
|
141
168
|
*/
|
|
142
|
-
async handleRemoteBroadcast(room, message) {
|
|
169
|
+
async handleRemoteBroadcast(room, message, metadata) {
|
|
143
170
|
const connectionIds = this._roomManager.getConnectionsInRoom(room);
|
|
144
171
|
for (const connId of connectionIds) {
|
|
145
172
|
if (this.isDuplicateMessage(connId, message.id)) {
|
|
146
173
|
continue;
|
|
147
174
|
}
|
|
175
|
+
if (this.preDeliveryFilter) {
|
|
176
|
+
const allowed = await this.preDeliveryFilter(connId, message, metadata);
|
|
177
|
+
if (!allowed)
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
148
180
|
await this.sendToConnection(connId, message);
|
|
149
181
|
}
|
|
150
182
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SSERoomBroadcaster.js","sourceRoot":"","sources":["../../../../lib/sse/rooms/SSERoomBroadcaster.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAOxC,8EAA8E;AAC9E,MAAM,oBAAoB,GAAG,IAAI,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,kBAAkB;IACZ,YAAY,CAAgB;IAC5B,OAAO,GAA8D,EAAE,CAAA;IACvE,UAAU,GAA6B,IAAI,GAAG,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"SSERoomBroadcaster.js","sourceRoot":"","sources":["../../../../lib/sse/rooms/SSERoomBroadcaster.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAOxC,8EAA8E;AAC9E,MAAM,oBAAoB,GAAG,IAAI,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,kBAAkB;IACZ,YAAY,CAAgB;IAC5B,OAAO,GAA8D,EAAE,CAAA;IACvE,UAAU,GAA6B,IAAI,GAAG,EAAE,CAAA;IACzD,iBAAiB,CAAoB;IAE7C,YAAY,IAAwC;QAClD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,cAAc,CAAA;QAEvC,oFAAoF;QACpF,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE;YAC3E,OAAO,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;QAC5D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;OAMG;IACH,oBAAoB,CAAC,MAAyB;QAC5C,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,gGAAgG,CACjG,CAAA;QACH,CAAC;QACD,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAA;IACjC,CAAC;IAED;;;OAGG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,YAAY,CAAA;IAC1B,CAAC;IAED;;;OAGG;IACH,cAAc,CAAC,MAA6D;QAC1E,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC3B,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,eAAe,CACnB,IAAuB,EACvB,KAAoC,EACpC,IAAgB,EAChB,OAIC;QAED,MAAM,OAAO,GAAe;YAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,IAAI;YACJ,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,UAAU,EAAE;YAC/B,KAAK,EAAE,OAAO,EAAE,KAAK;SACtB,CAAA;QACD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QACzE,OAAO,SAAS,CAAA;IAClB,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,gBAAgB,CACpB,IAAuB,EACvB,OAAmB,EACnB,OAAuE;QAEvE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACjD,MAAM,aAAa,GAAG,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAA;QAExD,IAAI,SAAS,GAAG,CAAC,CAAA;QACjB,IAAI,QAAQ,GAAG,CAAC,CAAA;QAChB,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;YACnC,mCAAmC;YACnC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;gBAChF,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,QAAQ,EAAE,CAAA;oBACV,SAAQ;gBACV,CAAC;YACH,CAAC;YACD,IAAI,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;gBACjD,SAAS,EAAE,CAAA;YACb,CAAC;QACH,CAAC;QAED,oEAAoE;QACpE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;YACpB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;YACzE,CAAC;QACH,CAAC;QAED,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAA;IAChC,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,IAAY;QAC/B,OAAO,IAAI,CAAC,YAAY,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;IACrD,CAAC;IAED;;;;;OAKG;IACH,wBAAwB,CAAC,IAAY;QACnC,OAAO,IAAI,CAAC,YAAY,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAA;IACzD,CAAC;IAED;;;OAGG;IACH,iBAAiB,CAAC,YAAoB;QACpC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IACtC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAAC,MAAc,EAAE,GAAe;QAC5D,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,qBAAqB,CACjC,IAAY,EACZ,OAAmB,EACnB,QAAkC;QAElC,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;QAClE,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;YACnC,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;gBAChD,SAAQ;YACV,CAAC;YACD,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;gBACvE,IAAI,CAAC,OAAO;oBAAE,SAAQ;YACxB,CAAC;YACD,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAC9C,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,kBAAkB,CAAC,YAAoB,EAAE,SAA6B;QAC5E,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,IAAI,GAAG,EAAE,CAAA;YACjB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,CAAA;QAC1C,CAAC;QAED,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,oDAAoD;QACpD,IAAI,KAAK,CAAC,IAAI,IAAI,oBAAoB,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAe,CAAA;YACpD,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACtB,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACpB,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;OAEG;IACK,sBAAsB,CAAC,KAAe;QAC5C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAA;QACvC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/D,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YAC3B,CAAC;QACH,CAAC;QACD,OAAO,aAAa,CAAA;IACtB,CAAC;CACF"}
|
|
@@ -128,7 +128,10 @@ export declare class SSERoomManager {
|
|
|
128
128
|
*
|
|
129
129
|
* @param room - The room to publish to
|
|
130
130
|
* @param message - The SSE message to broadcast
|
|
131
|
-
* @param options - Broadcast options
|
|
131
|
+
* @param options - Broadcast options (e.g. `local: true` to skip adapter)
|
|
132
|
+
* @param metadata - Optional opaque metadata propagated to other nodes
|
|
133
|
+
* alongside the message. Used by `SSESubscriptionManager` for cross-node
|
|
134
|
+
* resolver evaluation; not delivered to clients.
|
|
132
135
|
*/
|
|
133
|
-
publish(room: string, message: SSEMessage, options?: RoomBroadcastOptions): Promise<void>;
|
|
136
|
+
publish(room: string, message: SSEMessage, options?: RoomBroadcastOptions, metadata?: Record<string, unknown>): Promise<void>;
|
|
134
137
|
}
|
|
@@ -51,12 +51,12 @@ export class SSERoomManager {
|
|
|
51
51
|
this.adapter = config.adapter ?? new InMemoryAdapter();
|
|
52
52
|
this.nodeId = config.nodeId ?? randomUUID();
|
|
53
53
|
// Set up adapter message handler
|
|
54
|
-
this.adapter.onMessage((room, message, sourceNodeId) => {
|
|
54
|
+
this.adapter.onMessage((room, message, sourceNodeId, metadata) => {
|
|
55
55
|
// Skip messages from this node (we already handled them locally)
|
|
56
56
|
if (sourceNodeId === this.nodeId) {
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
|
-
this.messageHandler?.(room, message, sourceNodeId);
|
|
59
|
+
return this.messageHandler?.(room, message, sourceNodeId, metadata);
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
@@ -222,13 +222,16 @@ export class SSERoomManager {
|
|
|
222
222
|
*
|
|
223
223
|
* @param room - The room to publish to
|
|
224
224
|
* @param message - The SSE message to broadcast
|
|
225
|
-
* @param options - Broadcast options
|
|
225
|
+
* @param options - Broadcast options (e.g. `local: true` to skip adapter)
|
|
226
|
+
* @param metadata - Optional opaque metadata propagated to other nodes
|
|
227
|
+
* alongside the message. Used by `SSESubscriptionManager` for cross-node
|
|
228
|
+
* resolver evaluation; not delivered to clients.
|
|
226
229
|
*/
|
|
227
|
-
async publish(room, message, options) {
|
|
230
|
+
async publish(room, message, options, metadata) {
|
|
228
231
|
if (options?.local) {
|
|
229
232
|
return; // Skip adapter for local-only broadcasts
|
|
230
233
|
}
|
|
231
|
-
await this.adapter.publish(room, message);
|
|
234
|
+
await this.adapter.publish(room, message, metadata);
|
|
232
235
|
}
|
|
233
236
|
}
|
|
234
237
|
//# sourceMappingURL=SSERoomManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SSERoomManager.js","sourceRoot":"","sources":["../../../../lib/sse/rooms/SSERoomManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAQ/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,OAAO,cAAc;IACzB,gDAAgD;IAC/B,eAAe,GAA6B,IAAI,GAAG,EAAE,CAAA;IAEtE,gDAAgD;IAC/B,eAAe,GAA6B,IAAI,GAAG,EAAE,CAAA;IAEtE,2CAA2C;IAClC,OAAO,CAAgB;IAEhC,sCAAsC;IAC7B,MAAM,CAAQ;IAEvB,+CAA+C;IACvC,cAAc,CAAwB;IAE9C,YAAY,SAA+B,EAAE;QAC3C,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,eAAe,EAAE,CAAA;QACtD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,UAAU,EAAE,CAAA;QAE3C,iCAAiC;QACjC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"SSERoomManager.js","sourceRoot":"","sources":["../../../../lib/sse/rooms/SSERoomManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,6 @@ export declare class InMemoryAdapter implements SSERoomAdapter {
|
|
|
13
13
|
disconnect(): Promise<void>;
|
|
14
14
|
subscribe(_room: string): Promise<void>;
|
|
15
15
|
unsubscribe(_room: string): Promise<void>;
|
|
16
|
-
publish(_room: string, _message: SSEMessage): Promise<void>;
|
|
16
|
+
publish(_room: string, _message: SSEMessage, _metadata?: Record<string, unknown>): Promise<void>;
|
|
17
17
|
onMessage(_handler: SSERoomMessageHandler): void;
|
|
18
18
|
}
|
|
@@ -23,7 +23,7 @@ export class InMemoryAdapter {
|
|
|
23
23
|
// No-op for in-memory adapter
|
|
24
24
|
return Promise.resolve();
|
|
25
25
|
}
|
|
26
|
-
publish(_room, _message) {
|
|
26
|
+
publish(_room, _message, _metadata) {
|
|
27
27
|
// No-op for in-memory adapter - messages are only sent locally
|
|
28
28
|
// The controller handles local delivery directly
|
|
29
29
|
return Promise.resolve();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InMemoryAdapter.js","sourceRoot":"","sources":["../../../../../lib/sse/rooms/adapters/InMemoryAdapter.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,OAAO,eAAe;IAC1B,OAAO;QACL,8BAA8B;QAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,UAAU;QACR,8BAA8B;QAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,kEAAkE;QAClE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,8BAA8B;QAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,OAAO,CAAC,KAAa,EAAE,QAAoB;
|
|
1
|
+
{"version":3,"file":"InMemoryAdapter.js","sourceRoot":"","sources":["../../../../../lib/sse/rooms/adapters/InMemoryAdapter.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,OAAO,eAAe;IAC1B,OAAO;QACL,8BAA8B;QAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,UAAU;QACR,8BAA8B;QAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,kEAAkE;QAClE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,8BAA8B;QAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,OAAO,CAAC,KAAa,EAAE,QAAoB,EAAE,SAAmC;QAC9E,+DAA+D;QAC/D,iDAAiD;QACjD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,SAAS,CAAC,QAA+B;QACvC,wDAAwD;IAC1D,CAAC;CACF"}
|
|
@@ -2,4 +2,4 @@ export { InMemoryAdapter } from './adapters/InMemoryAdapter.js';
|
|
|
2
2
|
export { defineRoom } from './defineRoom.js';
|
|
3
3
|
export { SSERoomBroadcaster } from './SSERoomBroadcaster.js';
|
|
4
4
|
export { SSERoomManager } from './SSERoomManager.js';
|
|
5
|
-
export type { RoomBroadcastOptions, RoomNameResolver, SSERoomAdapter, SSERoomManagerConfig, SSERoomMessageHandler, SSERoomOperations, } from './types.js';
|
|
5
|
+
export type { PreDeliveryFilter, RoomBroadcastOptions, RoomNameResolver, SSERoomAdapter, SSERoomManagerConfig, SSERoomMessageHandler, SSERoomOperations, } from './types.js';
|
|
@@ -63,8 +63,11 @@ export type SSERoomAdapter = {
|
|
|
63
63
|
*
|
|
64
64
|
* @param room - The room to publish to
|
|
65
65
|
* @param message - The SSE message to broadcast
|
|
66
|
+
* @param metadata - Optional opaque metadata for cross-node subscription
|
|
67
|
+
* filtering (e.g. event scope, project id). Adapters serialize this
|
|
68
|
+
* alongside the message; it is not delivered to SSE clients.
|
|
66
69
|
*/
|
|
67
|
-
publish(room: string, message: SSEMessage): Promise<void>;
|
|
70
|
+
publish(room: string, message: SSEMessage, metadata?: Record<string, unknown>): Promise<void>;
|
|
68
71
|
/**
|
|
69
72
|
* Register a handler for messages received from other nodes.
|
|
70
73
|
* The handler should forward the message to local connections in the room.
|
|
@@ -75,8 +78,37 @@ export type SSERoomAdapter = {
|
|
|
75
78
|
};
|
|
76
79
|
/**
|
|
77
80
|
* Handler for messages received from the adapter (other nodes).
|
|
81
|
+
*
|
|
82
|
+
* @param room - The room the message was published to
|
|
83
|
+
* @param message - The SSE message
|
|
84
|
+
* @param sourceNodeId - Id of the node that originated the publish; used to
|
|
85
|
+
* suppress echoing the publisher's own messages back to it
|
|
86
|
+
* @param metadata - Optional opaque metadata attached at publish time;
|
|
87
|
+
* `undefined` for v1 (pre-metadata) messages from older nodes
|
|
88
|
+
*/
|
|
89
|
+
export type SSERoomMessageHandler = (room: string, message: SSEMessage, sourceNodeId: string, metadata?: Record<string, unknown>) => void | Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Pre-delivery filter called before sending to each connection.
|
|
92
|
+
* Returns true to deliver, false to skip.
|
|
93
|
+
*
|
|
94
|
+
* Used by SSESubscriptionManager to inject resolver pipeline evaluation
|
|
95
|
+
* into the broadcaster's delivery path.
|
|
96
|
+
*
|
|
97
|
+
* When no filter is set, all connections receive all messages (existing behavior).
|
|
78
98
|
*/
|
|
79
|
-
export type
|
|
99
|
+
export type PreDeliveryFilter = (connectionId: string, message: SSEMessage, metadata?: Record<string, unknown>) => Promise<boolean> | boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Result of a single broadcast call.
|
|
102
|
+
*
|
|
103
|
+
* Returned per-call (rather than tracked on the broadcaster) so that concurrent
|
|
104
|
+
* broadcasts cannot interleave their counters.
|
|
105
|
+
*/
|
|
106
|
+
export type BroadcastResult = {
|
|
107
|
+
/** Local connections the message was successfully sent to. */
|
|
108
|
+
delivered: number;
|
|
109
|
+
/** Local connections skipped by the pre-delivery filter. */
|
|
110
|
+
filtered: number;
|
|
111
|
+
};
|
|
80
112
|
/**
|
|
81
113
|
* Configuration for the SSE Room Manager.
|
|
82
114
|
*/
|