Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@cap-js-community/websocket

Package Overview
Dependencies
Maintainers
7
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cap-js-community/websocket - npm Package Compare versions

Comparing version 0.8.0 to 0.8.1

10

CHANGELOG.md

@@ -8,2 +8,12 @@ # Changelog

## Version 0.8.1 - 2024-03-04
### Added
- Allows to provide event emit headers to dynamically control websocket processing without annotations
### Fixed
- Describe the usage of CDS persistent outbox for websocket events
## Version 0.8.0 - 2024-02-15

@@ -10,0 +20,0 @@

16

package.json
{
"name": "@cap-js-community/websocket",
"version": "0.8.0",
"version": "0.8.1",
"description": "WebSocket adapter for CDS",

@@ -46,3 +46,3 @@ "homepage": "https://cap.cloud.sap/",

"cookie": "^0.6.0",
"express": "^4.18.2",
"express": "^4.18.3",
"redis": "^4.6.13",

@@ -54,12 +54,12 @@ "socket.io": "^4.7.4",

"@cap-js-community/websocket": "./",
"@cap-js/sqlite": "^1.5.0",
"@sap/cds": "^7.6.3",
"@sap/cds-dk": "^7.6.1",
"@cap-js/sqlite": "^1.5.1",
"@sap/cds": "^7.7.0",
"@sap/cds-dk": "^7.7.0",
"@sap/xssec": "3.6.1",
"@socket.io/redis-adapter": "^8.2.1",
"@socket.io/redis-streams-adapter": "^0.1.0",
"@socket.io/redis-streams-adapter": "^0.2.0",
"@types/express": "^4.17.21",
"eslint": "^8.54.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-jest": "^27.8.0",
"eslint-plugin-jest": "^27.9.0",
"jest": "^29.7.0",

@@ -66,0 +66,0 @@ "passport": "0.7.0",

@@ -223,2 +223,43 @@ # @cap-js-community/websocket

### Transactional Safety
In most situations only websocket events shall be broadcast, in case the primary transaction succeeded.
It can be done manually, by emitting CDS event as part of the `req.on("succeeded")` handler.
```js
req.on("succeeded", async () => {
await srv.emit("received", req.data);
});
```
Alternatively you can leverage the CAP in-memory outbox via `cds.outboxed` as follows:
```js
const chatService = cds.outboxed(await cds.connect.to("ChatService"));
await chatService.emit("received", req.data);
```
This has the benefit, that the event emitting is coupled to the success of the primary transaction.
Still the asynchronous event processing could fail, and would not be retried anymore.
That's where the CDS persistent outbox comes into play.
#### CDS Persistent Outbox
Websocket events can also be sent via the CDS persistent outbox. That means, the CDS events triggering the websocket broadcast
are added to the CDS persistent outbox when the primary transaction succeeded. The events are processed asynchronously
and transactional safe in a separate transaction. It is ensured, that the event is processed in any case, as outbox keeps the
outbox entry open, until the event processing succeeded.
The transactional safety can be achieved using `cds.outboxed` with kind `persistent-outbox` as follows:
```js
const chatService = cds.outboxed(await cds.connect.to("ChatService"), {
kind: "persistent-outbox",
});
await chatService.emit("received", req.data);
```
In that case, the websocket event is broadcast to websocket clients exactly once, when the primary transaction succeeds.
In case of execution errors, the event broadcast is retried automatically, while processing the persistent outbox.
### Event User

@@ -320,2 +361,22 @@

### Event Emit Headers
The websocket implementation allows to provide event emit headers to dynamically control websocket processing.
The following headers are available:
- `excludeCurrentUser: boolean`: Exclude current user from event broadcasting (see section Event User)
- `contexts: String[]`: Provide an array of context strings to identify a subset of clients (see section Event Contexts)
Emitting events with headers can be performed as follows:
```js
await srv.emit("customContextHeaderEvent", req.data, {
contexts: ["..."],
excludeCurrentUser: req.data.type === "1",
});
```
The respective event annotations (described in sections above) are respected in addition to event emit header specification,
so that primitive typed values have priority when specified as part of headers and array-like data is unified.
### Connect & Disconnect

@@ -322,0 +383,0 @@

@@ -139,6 +139,6 @@ "use strict";

service.on(event, async (req) => {
const localEventName = serviceLocalName(service, event.name);
try {
const user = deriveUser(event, req.data, req);
const contexts = deriveContexts(event, req.data);
const localEventName = serviceLocalName(service, event.name);
const user = deriveUser(event, req.data, req.headers, req);
const contexts = deriveContexts(event, req.data, req.headers);
await socketServer.broadcast({

@@ -297,4 +297,4 @@ service: servicePath,

if (eventDefinition) {
user = deriveUser(eventDefinition, data, socket);
contexts = deriveContexts(eventDefinition, data);
user = deriveUser(eventDefinition, data, {}, socket);
contexts = deriveContexts(eventDefinition, data, {});
}

@@ -345,3 +345,9 @@ const contentData = broadcastData(entity, data, eventDefinition);

function deriveUser(event, data, req) {
function deriveUser(event, data, headers, req) {
if (headers?.excludeCurrentUser !== undefined) {
if (headers?.excludeCurrentUser) {
return req.context.user.id;
}
return;
}
let user =

@@ -369,5 +375,5 @@ event["@websocket.user"] || event["@ws.user"] || event["@websocket.broadcast.user"] || event["@ws.broadcast.user"];

function deriveContexts(event, data) {
const contexts = [];
let isContextEvent = false;
function deriveContexts(event, data, headers) {
let isContextEvent = !!Array.isArray(headers?.contexts);
const contexts = isContextEvent ? headers.contexts : [];
if (event.elements) {

@@ -374,0 +380,0 @@ for (const name in event.elements) {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc