@opencxh/domain 1.84.0 → 1.86.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/entities/activity/types.d.ts +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10 -9
- package/dist/platform/communication.d.ts +62 -0
- package/dist/platform/presence.d.ts +25 -0
- package/dist/platform/storage.d.ts +19 -0
- package/package.json +1 -1
|
@@ -161,6 +161,8 @@ export type FileUploadedPayload = {
|
|
|
161
161
|
fileUrl: string;
|
|
162
162
|
fileType: string;
|
|
163
163
|
size: number;
|
|
164
|
+
/** Storage FilePointer id, when the file lives in the storage app (download via storage.file.{id}.download). */
|
|
165
|
+
fileId?: string;
|
|
164
166
|
};
|
|
165
167
|
export type InteractionStatusChangedPayload = {
|
|
166
168
|
fromStatus: "open" | "pending" | "closed";
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=140;function i(e){return e.replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<[^>]+>/g," ").replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/\s+/g," ").trim()}function A(e){const t=e.trim();return t.length<=l?t:t.slice(0,l-1).trimEnd()+"…"}function
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=140;function i(e){return e.replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<[^>]+>/g," ").replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/\s+/g," ").trim()}function A(e){const t=e.trim();return t.length<=l?t:t.slice(0,l-1).trimEnd()+"…"}function o(e){if(!e||e<0)return"";const t=Math.floor(e/60),n=Math.floor(e%60);return`${t}:${n.toString().padStart(2,"0")}`}function I(e){switch(e.type){case"EMAIL_RECEIVED":case"EMAIL_SENT":{const t=e.payload,n=t.bodySnippet?.trim()||i(t.body??"");return t.subject?`${t.subject} — ${n}`:n}case"CHAT_MESSAGE_SENT":case"CHAT_MESSAGE_RECEIVED":return e.payload.text??"";case"CHAT_RENAMED":return`Hernoemd naar "${e.payload.newName}"`;case"CHAT_MEMBER_JOINED":return`${e.payload.members.map(t=>t.name).join(", ")} toegevoegd`;case"CHAT_MEMBER_LEFT":return`${e.payload.members.map(t=>t.name).join(", ")} verlaten`;case"CHAT_CALL_STARTED":return"Gesprek gestart";case"CHAT_CALL_ENDED":return`Gesprek beëindigd${e.payload.duration?` (${o(e.payload.duration)})`:""}`;case"CHAT_EVENT":return e.payload.text??e.payload.eventType;case"VOICE_CALL_STARTED":case"VIDEO_CALL_STARTED":return"Gesprek gestart";case"VOICE_CALL_ANSWERED":case"VIDEO_CALL_ANSWERED":return"Gesprek aangenomen";case"VOICE_CALL_HOLD":case"VIDEO_CALL_HOLD":return"In de wacht";case"VOICE_CALL_UNHOLD":case"VIDEO_CALL_UNHOLD":return"Hervat";case"VOICE_CALL_ENDED":case"VIDEO_CALL_ENDED":return`Gesprek beëindigd${e.payload.duration?` (${o(e.payload.duration)})`:""}`;case"VOICE_CALL_MISSED":case"VIDEO_CALL_MISSED":return"Gemiste oproep";case"VOICE_CALL_FAILED":case"VIDEO_CALL_FAILED":return"Gesprek mislukt";case"VOICE_CALL_VOICEMAIL":return e.payload.transcription?.trim()?e.payload.transcription:"Voicemail ontvangen";case"TRANSCRIPT_ADDED":return(e.payload.segments??[]).map(t=>t.text).join(" ");case"AI_MESSAGE_ADDED":return e.payload.output?.text??e.payload.input?.text??"";case"MEETING_SCHEDULED":return`Vergadering gepland: ${e.payload.title}`;case"MEETING_STARTED":return`Vergadering gestart: ${e.payload.title}`;case"MEETING_ENDED":return`Vergadering beëindigd${e.payload.duration?` (${o(e.payload.duration)})`:""}`;case"MEETING_PARTICIPANT_JOINED":return`${e.payload.name} deelgenomen`;case"MEETING_PARTICIPANT_LEFT":return`${e.payload.name} verlaten`;case"COMMENT_ADDED":return e.payload.text??"";case"FILE_UPLOADED":return`Bestand: ${e.payload.fileName}`;case"INTERACTION_CREATED":return"Interactie aangemaakt";case"INTERACTION_STATUS_CHANGED":return`Status: ${e.payload.fromStatus} → ${e.payload.toStatus}`;case"INTERACTION_ASSIGNED":return"Interactie toegewezen";default:return""}}function _(e){return{activityId:e.id,type:e.type,snippet:A(I(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function T(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function g(e){return e.endpoints??[]}const D=e=>!!e.assignedUserId||!!e.assignedInboxId,S=e=>e.status==="closed",f=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,N=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function R(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}function u(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},s=t.intents.filter(a=>!n.has(a.intent)).map(a=>O(a,r[a.intent]));if(!e.extraIntents||e.extraIntents.length===0)return s;const E=new Set(s.map(a=>a.intent));for(const a of e.extraIntents)E.has(a.intent)||(s.push(a),E.add(a.intent));return s}function c(e,t){const n={};for(const r of e.intents){if(!r.togglable)continue;const s=t?.[r.intent];n[r.intent]=s??r.defaultEnabled??!0}return n}function L(e,t){const n=c(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function O(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function C(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const s=t[r.providerId];if(s)for(const E of u(r,s))n.push({channel:r,description:s,capability:E})}return n}function M(e,t){return t.filter(n=>n.capability.intent===e)}function d(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function b(e,t){return d(e.scheme,t)}function P(e,t,n){const r=[];for(const s of e)for(const E of n)E.capability.intent===t&&E.capability.targetSchemes.includes(s.scheme)&&r.push({channelIntent:E,endpoint:s});return r}var p=(e=>(e.MAILTO="mailto",e.SIP="sip",e.TEL="tel",e.WEBHOOK="webhook",e.USERNAME="username",e.ID="id",e.CUSTOM="custom",e.URL="url",e.TELEGRAM="telegram",e.WHATSAPP="whatsapp",e.MESSENGER="messenger",e.INSTAGRAM="instagram",e.VIBER="viber",e.SMS="sms",e.FAX="fax",e.TEAMS="teams",e.CALENDAR="calendar",e))(p||{});const G="message_window",U="message_templates",V={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},m=(e,t)=>({intent:e,...t}),y="folder_management",H="remote_search",h={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},$=9e4,F="installation-id";exports.CommunicationScheme=p;exports.FEATURE_FOLDER_MANAGEMENT=y;exports.FEATURE_REMOTE_SEARCH=H;exports.INSTALLATION_HEADER=F;exports.InteractionParticipantRole=V;exports.PRESENCE_ONLINE_WINDOW_MS=$;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=U;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=G;exports.UNSUPPORTED=h;exports.buildActivityPreview=_;exports.buildChannelIntents=C;exports.defineIntent=m;exports.disabledIntentsFromCapabilities=L;exports.filterByEndpoint=b;exports.filterByIntent=M;exports.filterByTargetScheme=d;exports.getContactEndpoints=g;exports.getShortTitle=N;exports.getUrgencyScore=f;exports.isAssigned=D;exports.isClosed=S;exports.isInteractionUnseen=R;exports.matchContactToIntents=P;exports.resolveAccountCapabilities=c;exports.resolveChannelIntents=u;exports.resolveSignature=T;exports.stripHtml=i;
|
package/dist/index.d.ts
CHANGED
|
@@ -44,5 +44,6 @@ export * from './platform/ui';
|
|
|
44
44
|
export * from './platform/media';
|
|
45
45
|
export * from './platform/provider';
|
|
46
46
|
export * from './platform/push';
|
|
47
|
+
export * from './platform/presence';
|
|
47
48
|
export * from './platform/installation';
|
|
48
49
|
export * from './platform/storage';
|
package/dist/index.js
CHANGED
|
@@ -100,7 +100,7 @@ function T(e, t) {
|
|
|
100
100
|
function g(e) {
|
|
101
101
|
return e.endpoints ?? [];
|
|
102
102
|
}
|
|
103
|
-
const
|
|
103
|
+
const D = (e) => !!e.assignedUserId || !!e.assignedInboxId, f = (e) => e.status === "closed", L = (e) => ({
|
|
104
104
|
urgent: 10,
|
|
105
105
|
high: 5,
|
|
106
106
|
normal: 2,
|
|
@@ -137,7 +137,7 @@ function A(e, t) {
|
|
|
137
137
|
transport: t.transport ?? e.transport
|
|
138
138
|
} : e;
|
|
139
139
|
}
|
|
140
|
-
function
|
|
140
|
+
function O(e, t) {
|
|
141
141
|
const n = [];
|
|
142
142
|
for (const r of e) {
|
|
143
143
|
if (!r.enabled) continue;
|
|
@@ -148,7 +148,7 @@ function C(e, t) {
|
|
|
148
148
|
}
|
|
149
149
|
return n;
|
|
150
150
|
}
|
|
151
|
-
function
|
|
151
|
+
function C(e, t) {
|
|
152
152
|
return t.filter((n) => n.capability.intent === e);
|
|
153
153
|
}
|
|
154
154
|
function i(e, t) {
|
|
@@ -182,28 +182,29 @@ const P = "message_window", V = "message_templates", G = {
|
|
|
182
182
|
// Voice/conference
|
|
183
183
|
HOST: "host",
|
|
184
184
|
PARTICIPANT: "participant"
|
|
185
|
-
}, H = (e, t) => ({ intent: e, ...t }), $ = "folder_management", y = "remote_search", U = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, m = "installation-id";
|
|
185
|
+
}, H = (e, t) => ({ intent: e, ...t }), $ = "folder_management", y = "remote_search", U = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, m = 9e4, h = "installation-id";
|
|
186
186
|
export {
|
|
187
187
|
I as CommunicationScheme,
|
|
188
188
|
$ as FEATURE_FOLDER_MANAGEMENT,
|
|
189
189
|
y as FEATURE_REMOTE_SEARCH,
|
|
190
|
-
|
|
190
|
+
h as INSTALLATION_HEADER,
|
|
191
191
|
G as InteractionParticipantRole,
|
|
192
|
+
m as PRESENCE_ONLINE_WINDOW_MS,
|
|
192
193
|
V as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
|
|
193
194
|
P as PROVIDER_FEATURE_MESSAGE_WINDOW,
|
|
194
195
|
U as UNSUPPORTED,
|
|
195
196
|
_ as buildActivityPreview,
|
|
196
|
-
|
|
197
|
+
O as buildChannelIntents,
|
|
197
198
|
H as defineIntent,
|
|
198
199
|
R as disabledIntentsFromCapabilities,
|
|
199
200
|
M as filterByEndpoint,
|
|
200
|
-
|
|
201
|
+
C as filterByIntent,
|
|
201
202
|
i as filterByTargetScheme,
|
|
202
203
|
g as getContactEndpoints,
|
|
203
204
|
N as getShortTitle,
|
|
204
205
|
L as getUrgencyScore,
|
|
205
|
-
|
|
206
|
-
|
|
206
|
+
D as isAssigned,
|
|
207
|
+
f as isClosed,
|
|
207
208
|
S as isInteractionUnseen,
|
|
208
209
|
b as matchContactToIntents,
|
|
209
210
|
c as resolveAccountCapabilities,
|
|
@@ -294,6 +294,68 @@ export interface RemoteStreamEvent {
|
|
|
294
294
|
speaker?: 'local' | 'remote';
|
|
295
295
|
label?: string;
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Peer-to-peer WebRTC signaling envelope. Exchanged between two users in the
|
|
299
|
+
* same tenant to negotiate a direct call — the media (audio) flows P2P; only
|
|
300
|
+
* these small SDP/ICE setup messages transit the server (relayed 1:1 via
|
|
301
|
+
* `Bridge.sse.push`). Transient: never persisted.
|
|
302
|
+
*
|
|
303
|
+
* - `offer`/`answer` → `data` is an `RTCSessionDescriptionInit`.
|
|
304
|
+
* - `ice` → `data` is an `RTCIceCandidateInit` (or `null` = end-of-candidates).
|
|
305
|
+
* - `bye` → `data` omitted; the sender tore the call down.
|
|
306
|
+
*/
|
|
307
|
+
export type WebRtcSignalKind = "offer" | "answer" | "ice" | "bye" | "request";
|
|
308
|
+
/** Request body the initiator/answerer POSTs to the signaling relay. */
|
|
309
|
+
export interface WebRtcSignalRequest {
|
|
310
|
+
/** Target userId (the peer this signal is meant for). */
|
|
311
|
+
to: string;
|
|
312
|
+
/** Stable call id, minted by the caller on invite. */
|
|
313
|
+
sessionId: string;
|
|
314
|
+
kind: WebRtcSignalKind;
|
|
315
|
+
data?: unknown;
|
|
316
|
+
/** Sender display name, so the peer can label the call UI without a lookup. */
|
|
317
|
+
fromName?: string;
|
|
318
|
+
/**
|
|
319
|
+
* Which peer connection this signal belongs to. `call` (default) is an audio
|
|
320
|
+
* session handled by the transport provider; `file` is an AirDrop-style
|
|
321
|
+
* RTCDataChannel transfer handled by the file-beam service; `monitor` is a
|
|
322
|
+
* one-way "listen along" forward handled by the call-monitor service. Lets
|
|
323
|
+
* one relay serve all three without the connection types colliding.
|
|
324
|
+
*/
|
|
325
|
+
channel?: "call" | "file" | "monitor";
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Broadcast (event `webrtc:callstate`) telling the tenant whether a user is
|
|
329
|
+
* currently on a call, so a supervisor can see who is available to listen in.
|
|
330
|
+
*/
|
|
331
|
+
export interface CallStateChange {
|
|
332
|
+
userId: string;
|
|
333
|
+
inCall: boolean;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* One ICE server entry. Structurally a subset of the browser's `RTCIceServer`,
|
|
337
|
+
* but kept DOM-free so the comm server can build it too (server tsconfig has no
|
|
338
|
+
* DOM lib). The client passes these straight to `new RTCPeerConnection`.
|
|
339
|
+
*/
|
|
340
|
+
export interface IceServerConfig {
|
|
341
|
+
urls: string | string[];
|
|
342
|
+
username?: string;
|
|
343
|
+
credential?: string;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Response of `GET /webrtc/ice`: the STUN list plus TURN servers with
|
|
347
|
+
* short-lived (per-user, time-limited) credentials, and how long they last.
|
|
348
|
+
*/
|
|
349
|
+
export interface IceConfigResponse {
|
|
350
|
+
iceServers: IceServerConfig[];
|
|
351
|
+
/** Seconds the TURN credentials remain valid. */
|
|
352
|
+
ttl: number;
|
|
353
|
+
}
|
|
354
|
+
/** What the callee receives over SSE — same envelope, server stamps `from`. */
|
|
355
|
+
export interface WebRtcSignalEvent extends WebRtcSignalRequest {
|
|
356
|
+
/** Sender userId, stamped by the relay from the authenticated subject. */
|
|
357
|
+
from: string;
|
|
358
|
+
}
|
|
297
359
|
/**
|
|
298
360
|
* Helper voor compactere intent-declaraties in `/provider/describe`.
|
|
299
361
|
* defineIntent("mail", { targetSchemes: [MAILTO], transport: { kind: "dispatch" } })
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presence — derived, not stored as its own entity. Online status is computed
|
|
3
|
+
* from `InstallationEntity.lastSeenAt`: a user is online when any of their
|
|
4
|
+
* non-revoked installations was seen within the freshness window. A light
|
|
5
|
+
* client heartbeat (~45s while the WebSocket is connected) keeps `lastSeenAt`
|
|
6
|
+
* fresh enough to reflect real reachability for calling.
|
|
7
|
+
*/
|
|
8
|
+
/** How recently a user's last device activity must be to count as online (ms). */
|
|
9
|
+
export declare const PRESENCE_ONLINE_WINDOW_MS = 90000;
|
|
10
|
+
/** One tenant user with their derived online status, as returned by GET /presence. */
|
|
11
|
+
export interface PresenceEntry {
|
|
12
|
+
userId: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
email?: string;
|
|
15
|
+
/** Max `lastSeenAt` across the user's non-revoked installations (ms epoch). */
|
|
16
|
+
lastSeenAt: number;
|
|
17
|
+
/** `now - lastSeenAt < PRESENCE_ONLINE_WINDOW_MS` at the time of the read. */
|
|
18
|
+
online: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** SSE payload pushed on an offline->online transition (event `presence:changed`). */
|
|
21
|
+
export interface PresenceChange {
|
|
22
|
+
userId: string;
|
|
23
|
+
online: boolean;
|
|
24
|
+
lastSeenAt: number;
|
|
25
|
+
}
|
|
@@ -35,6 +35,25 @@ export interface FilePointer {
|
|
|
35
35
|
createdAt?: number;
|
|
36
36
|
tags?: string[];
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* A field a storage provider needs to connect an account. Providers declare
|
|
40
|
+
* these (via /provider/describe); the accounts UI renders the form from them,
|
|
41
|
+
* so a new backend needs no UI changes.
|
|
42
|
+
*/
|
|
43
|
+
export interface StorageAccountField {
|
|
44
|
+
name: string;
|
|
45
|
+
label: string;
|
|
46
|
+
type: "text" | "password" | "number" | "checkbox";
|
|
47
|
+
required?: boolean;
|
|
48
|
+
placeholder?: string;
|
|
49
|
+
}
|
|
50
|
+
/** A connectable storage provider + the fields needed to connect an account. */
|
|
51
|
+
export interface StorageProviderInfo {
|
|
52
|
+
/** Storage-role provider id (e.g. base64("ftp:storage")). */
|
|
53
|
+
id: string;
|
|
54
|
+
displayName: string;
|
|
55
|
+
accountFields: StorageAccountField[];
|
|
56
|
+
}
|
|
38
57
|
/**
|
|
39
58
|
* A linked storage backend account (credentials to an S3 bucket, an FTP host, …).
|
|
40
59
|
* Backed by the canonical ManagedAccount store, mirroring the AI app's account
|