@opencxh/domain 1.17.0 → 1.18.1

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.
@@ -1,4 +1,4 @@
1
- import { Uri } from '../../platform/communication';
1
+ import { IntentCapability, Uri } from '../../platform/communication';
2
2
  export interface Channel {
3
3
  id: string;
4
4
  organizationId: string;
@@ -12,6 +12,9 @@ export interface Channel {
12
12
  ownerId: string;
13
13
  externalIds?: string[];
14
14
  settings: Record<string, any>;
15
+ disabledIntents?: string[];
16
+ intentOverrides?: Record<string, Partial<IntentCapability>>;
17
+ extraIntents?: IntentCapability[];
15
18
  enabled: boolean;
16
19
  verified: boolean;
17
20
  }
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var t=(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.VIBER="viber",e.SMS="sms",e.FAX="fax",e.TEAMS="teams",e))(t||{});function d(e){if(e.endpoints&&e.endpoints.length>0)return e.endpoints;const s=[];return e.phone&&s.push({scheme:t.TEL,resource:e.phone,primary:!0}),e.email&&s.push({scheme:t.MAILTO,resource:e.email,primary:!e.phone}),s}const p=e=>!!e.assignedUserId||!!e.assignedInboxId,a=e=>e.status==="closed",f=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,g=(e,s=30)=>e.title.length<=s?e.title:`${e.title.substring(0,s)}...`,c={[t.TEL]:["call"],[t.SIP]:["call"],[t.MAILTO]:["mail"],[t.SMS]:["message"],[t.WHATSAPP]:["message"],[t.TELEGRAM]:["message"],[t.VIBER]:["message"],[t.TEAMS]:["call","message","video"],[t.FAX]:["message"]},E=["call","mail","message","video"];function l(e,s){if(!e.supportedSchemes.includes(s))return[];const n=e.capabilities.filter(r=>r.scheme===s&&r.value===!0&&E.includes(r.key));return n.length>0?n.map(r=>r.key):c[s]??[]}function u(e){const s=new Set;for(const n of e.supportedSchemes)for(const r of l(e,n))s.add(r);return Array.from(s)}function T(e,s){return Object.entries(e).map(([n,r])=>({providerId:n,description:r,intents:u(r)}))}function A(e,s){return s.filter(n=>n.intents.includes(e))}function v(e,s){return s.filter(n=>n.description.supportedSchemes.includes(e.scheme))}function I(e,s,n){const r=[];for(const o of e)for(const i of n)i.description.supportedSchemes.includes(o.scheme)&&l(i.description,o.scheme).includes(s)&&r.push({capability:i,endpoint:o});return r}exports.CommunicationScheme=t;exports.buildResolvedCapabilities=T;exports.getContactEndpoints=d;exports.getShortTitle=g;exports.getUrgencyScore=f;exports.intentsForProvider=u;exports.intentsForScheme=l;exports.isAssigned=p;exports.isClosed=a;exports.resolveForContact=I;exports.resolveForEndpoint=v;exports.resolveForIntent=A;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var l=(t=>(t.MAILTO="mailto",t.SIP="sip",t.TEL="tel",t.WEBHOOK="webhook",t.USERNAME="username",t.ID="id",t.CUSTOM="custom",t.URL="url",t.TELEGRAM="telegram",t.WHATSAPP="whatsapp",t.VIBER="viber",t.SMS="sms",t.FAX="fax",t.TEAMS="teams",t))(l||{});function a(t){if(t.endpoints&&t.endpoints.length>0)return t.endpoints;const e=[];return t.phone&&e.push({scheme:l.TEL,resource:t.phone,primary:!0}),t.email&&e.push({scheme:l.MAILTO,resource:t.email,primary:!t.phone}),e}const p=t=>!!t.assignedUserId||!!t.assignedInboxId,d=t=>t.status==="closed",g=t=>({urgent:10,high:5,normal:2,low:1})[t.priority]||0,c=(t,e=30)=>t.title?.length<=e?t.title:`${t.title.substring(0,e)}...`;function f(t,e){const r=new Set(t.disabledIntents??[]),i=t.intentOverrides??{},n=e.intents.filter(s=>!r.has(s.intent)).map(s=>I(s,i[s.intent]));if(!t.extraIntents||t.extraIntents.length===0)return n;const o=new Set(n.map(s=>s.intent));for(const s of t.extraIntents)o.has(s.intent)||(n.push(s),o.add(s.intent));return n}function I(t,e){return e?{intent:e.intent??t.intent,targetSchemes:e.targetSchemes??t.targetSchemes,transport:e.transport??t.transport}:t}function h(t,e){const r=[];for(const i of t){if(!i.enabled)continue;const n=e[i.providerId];if(n)for(const o of f(i,n))r.push({channel:i,description:n,capability:o})}return r}function y(t,e){return e.filter(r=>r.capability.intent===t)}function u(t,e){return e.filter(r=>r.capability.targetSchemes.includes(t))}function T(t,e){return u(t.scheme,e)}function b(t,e,r){const i=[];for(const n of t)for(const o of r)o.capability.intent===e&&o.capability.targetSchemes.includes(n.scheme)&&i.push({channelIntent:o,endpoint:n});return i}exports.CommunicationScheme=l;exports.buildChannelIntents=h;exports.filterByEndpoint=T;exports.filterByIntent=y;exports.filterByTargetScheme=u;exports.getContactEndpoints=a;exports.getShortTitle=c;exports.getUrgencyScore=g;exports.isAssigned=p;exports.isClosed=d;exports.matchContactToIntents=b;exports.resolveChannelIntents=f;
package/dist/index.js CHANGED
@@ -1,70 +1,69 @@
1
- var n = /* @__PURE__ */ ((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.VIBER = "viber", e.SMS = "sms", e.FAX = "fax", e.TEAMS = "teams", e))(n || {});
2
- function f(e) {
3
- if (e.endpoints && e.endpoints.length > 0)
4
- return e.endpoints;
5
- const s = [];
6
- return e.phone && s.push({ scheme: n.TEL, resource: e.phone, primary: !0 }), e.email && s.push({ scheme: n.MAILTO, resource: e.email, primary: !e.phone }), s;
1
+ var f = /* @__PURE__ */ ((t) => (t.MAILTO = "mailto", t.SIP = "sip", t.TEL = "tel", t.WEBHOOK = "webhook", t.USERNAME = "username", t.ID = "id", t.CUSTOM = "custom", t.URL = "url", t.TELEGRAM = "telegram", t.WHATSAPP = "whatsapp", t.VIBER = "viber", t.SMS = "sms", t.FAX = "fax", t.TEAMS = "teams", t))(f || {});
2
+ function a(t) {
3
+ if (t.endpoints && t.endpoints.length > 0)
4
+ return t.endpoints;
5
+ const e = [];
6
+ return t.phone && e.push({ scheme: f.TEL, resource: t.phone, primary: !0 }), t.email && e.push({ scheme: f.MAILTO, resource: t.email, primary: !t.phone }), e;
7
7
  }
8
- const a = (e) => !!e.assignedUserId || !!e.assignedInboxId, g = (e) => e.status === "closed", E = (e) => ({
8
+ const d = (t) => !!t.assignedUserId || !!t.assignedInboxId, g = (t) => t.status === "closed", I = (t) => ({
9
9
  urgent: 10,
10
10
  high: 5,
11
11
  normal: 2,
12
12
  low: 1
13
- })[e.priority] || 0, T = (e, s = 30) => e.title.length <= s ? e.title : `${e.title.substring(0, s)}...`, u = {
14
- [n.TEL]: ["call"],
15
- [n.SIP]: ["call"],
16
- [n.MAILTO]: ["mail"],
17
- [n.SMS]: ["message"],
18
- [n.WHATSAPP]: ["message"],
19
- [n.TELEGRAM]: ["message"],
20
- [n.VIBER]: ["message"],
21
- [n.TEAMS]: ["call", "message", "video"],
22
- [n.FAX]: ["message"]
23
- }, d = ["call", "mail", "message", "video"];
24
- function i(e, s) {
25
- if (!e.supportedSchemes.includes(s)) return [];
26
- const t = e.capabilities.filter(
27
- (r) => r.scheme === s && r.value === !0 && d.includes(r.key)
28
- );
29
- return t.length > 0 ? t.map((r) => r.key) : u[s] ?? [];
13
+ })[t.priority] || 0, c = (t, e = 30) => t.title?.length <= e ? t.title : `${t.title.substring(0, e)}...`;
14
+ function l(t, e) {
15
+ const r = new Set(t.disabledIntents ?? []), i = t.intentOverrides ?? {}, n = e.intents.filter((s) => !r.has(s.intent)).map((s) => u(s, i[s.intent]));
16
+ if (!t.extraIntents || t.extraIntents.length === 0) return n;
17
+ const o = new Set(n.map((s) => s.intent));
18
+ for (const s of t.extraIntents)
19
+ o.has(s.intent) || (n.push(s), o.add(s.intent));
20
+ return n;
30
21
  }
31
- function p(e) {
32
- const s = /* @__PURE__ */ new Set();
33
- for (const t of e.supportedSchemes)
34
- for (const r of i(e, t)) s.add(r);
35
- return Array.from(s);
22
+ function u(t, e) {
23
+ return e ? {
24
+ intent: e.intent ?? t.intent,
25
+ targetSchemes: e.targetSchemes ?? t.targetSchemes,
26
+ transport: e.transport ?? t.transport
27
+ } : t;
36
28
  }
37
- function c(e, s) {
38
- return Object.entries(e).map(([t, r]) => ({
39
- providerId: t,
40
- description: r,
41
- intents: p(r)
42
- }));
29
+ function h(t, e) {
30
+ const r = [];
31
+ for (const i of t) {
32
+ if (!i.enabled) continue;
33
+ const n = e[i.providerId];
34
+ if (n)
35
+ for (const o of l(i, n))
36
+ r.push({ channel: i, description: n, capability: o });
37
+ }
38
+ return r;
43
39
  }
44
- function A(e, s) {
45
- return s.filter((t) => t.intents.includes(e));
40
+ function y(t, e) {
41
+ return e.filter((r) => r.capability.intent === t);
46
42
  }
47
- function I(e, s) {
48
- return s.filter((t) => t.description.supportedSchemes.includes(e.scheme));
43
+ function p(t, e) {
44
+ return e.filter((r) => r.capability.targetSchemes.includes(t));
49
45
  }
50
- function h(e, s, t) {
51
- const r = [];
52
- for (const l of e)
53
- for (const o of t)
54
- o.description.supportedSchemes.includes(l.scheme) && i(o.description, l.scheme).includes(s) && r.push({ capability: o, endpoint: l });
55
- return r;
46
+ function b(t, e) {
47
+ return p(t.scheme, e);
48
+ }
49
+ function E(t, e, r) {
50
+ const i = [];
51
+ for (const n of t)
52
+ for (const o of r)
53
+ o.capability.intent === e && o.capability.targetSchemes.includes(n.scheme) && i.push({ channelIntent: o, endpoint: n });
54
+ return i;
56
55
  }
57
56
  export {
58
- n as CommunicationScheme,
59
- c as buildResolvedCapabilities,
60
- f as getContactEndpoints,
61
- T as getShortTitle,
62
- E as getUrgencyScore,
63
- p as intentsForProvider,
64
- i as intentsForScheme,
65
- a as isAssigned,
57
+ f as CommunicationScheme,
58
+ h as buildChannelIntents,
59
+ b as filterByEndpoint,
60
+ y as filterByIntent,
61
+ p as filterByTargetScheme,
62
+ a as getContactEndpoints,
63
+ c as getShortTitle,
64
+ I as getUrgencyScore,
65
+ d as isAssigned,
66
66
  g as isClosed,
67
- h as resolveForContact,
68
- I as resolveForEndpoint,
69
- A as resolveForIntent
67
+ E as matchContactToIntents,
68
+ l as resolveChannelIntents
70
69
  };
@@ -1,31 +1,48 @@
1
+ import { Channel } from '../entities/channel/types';
1
2
  import { ContactEndpoint } from '../entities/contact/types';
2
- import { CommsAccount, CommunicationScheme, ProviderDescription, Uri } from './communication';
3
+ import { CommunicationScheme, IntentCapability, ProviderDescription, Uri } from './communication';
4
+ /**
5
+ * Reference list of intents that have built-in UI defaults (icons, labels,
6
+ * default composers). Intent itself is an open string — providers may
7
+ * declare anything. Unknown intents render as generic actions.
8
+ */
3
9
  export type CommunicationIntent = "call" | "mail" | "message" | "video";
4
- export interface ResolvedCapability {
5
- providerId: string;
10
+ /**
11
+ * A channel's effective intent — the resolved IntentCapability for this
12
+ * specific channel, ready for routing/UI decisions.
13
+ */
14
+ export interface ChannelIntent {
15
+ channel: Channel;
6
16
  description: ProviderDescription;
7
- intents: CommunicationIntent[];
17
+ capability: IntentCapability;
8
18
  }
9
- export interface ResolvedCapabilityMatch {
10
- capability: ResolvedCapability;
19
+ export interface ChannelIntentMatch {
20
+ channelIntent: ChannelIntent;
11
21
  endpoint: ContactEndpoint;
12
22
  }
13
23
  /**
14
- * Returns which intents a provider offers for a given scheme.
24
+ * Pure resolution of a channel's effective intents.
15
25
  *
16
- * Capability rows whose `key` is a known user-intent (`call|mail|message|video`)
17
- * are treated as explicit declarations. Anything else (e.g. `call_forward`,
18
- * `call_hold` those are session-action feature flags) is ignored here.
26
+ * Algorithm:
27
+ * 1. Start from `description.intents` (provider defaults)
28
+ * 2. Drop entries whose `intent` is in `channel.disabledIntents`
29
+ * 3. Shallow-merge `channel.intentOverrides[intent]` over the provider default
30
+ * 4. Append `channel.extraIntents` (deduped on `intent`)
19
31
  *
20
- * When a scheme has no explicit intent rows, the conventional default
21
- * mapping kicks in so providers can ship just `supportedSchemes`.
32
+ * No fallback heuristics, no scheme-based defaults provider declares the
33
+ * source of truth, channel patches it.
34
+ */
35
+ export declare function resolveChannelIntents(channel: Channel, description: ProviderDescription): IntentCapability[];
36
+ /**
37
+ * Builds the full set of (channel × intent) entries across all channels —
38
+ * the source for the starter palette.
22
39
  */
23
- export declare function intentsForScheme(description: ProviderDescription, scheme: CommunicationScheme): CommunicationIntent[];
40
+ export declare function buildChannelIntents(channels: Channel[], descriptions: Record<string, ProviderDescription>): ChannelIntent[];
41
+ export declare function filterByIntent(intent: string, items: ChannelIntent[]): ChannelIntent[];
42
+ export declare function filterByTargetScheme(scheme: CommunicationScheme, items: ChannelIntent[]): ChannelIntent[];
43
+ export declare function filterByEndpoint(uri: Uri, items: ChannelIntent[]): ChannelIntent[];
24
44
  /**
25
- * Unions intents across all schemes a provider supports.
45
+ * Cross-product of contact endpoints × channel intents for one specific
46
+ * intent. One match = one row in the starter palette / contact buttons.
26
47
  */
27
- export declare function intentsForProvider(description: ProviderDescription): CommunicationIntent[];
28
- export declare function buildResolvedCapabilities(descriptions: Record<string, ProviderDescription>, _accounts?: CommsAccount[]): ResolvedCapability[];
29
- export declare function resolveForIntent(intent: CommunicationIntent, capabilities: ResolvedCapability[]): ResolvedCapability[];
30
- export declare function resolveForEndpoint(uri: Uri, capabilities: ResolvedCapability[]): ResolvedCapability[];
31
- export declare function resolveForContact(endpoints: ContactEndpoint[], intent: CommunicationIntent, capabilities: ResolvedCapability[]): ResolvedCapabilityMatch[];
48
+ export declare function matchContactToIntents(endpoints: ContactEndpoint[], intent: string, items: ChannelIntent[]): ChannelIntentMatch[];
@@ -40,11 +40,7 @@ export interface ITransportProvider {
40
40
  setOutgoingStream(sessionId: string, stream: MediaStream | null): Promise<void>;
41
41
  handleAction(sessionId: string, actionId: string, ...args: any[]): void;
42
42
  }
43
- export interface Capability {
44
- scheme: string;
45
- key: string;
46
- value: boolean;
47
- }
43
+ export type CommunicationDispatch = "transport" | "compose";
48
44
  export declare enum CommunicationScheme {
49
45
  MAILTO = "mailto",
50
46
  SIP = "sip",
@@ -61,11 +57,63 @@ export declare enum CommunicationScheme {
61
57
  FAX = "fax",
62
58
  TEAMS = "teams"
63
59
  }
60
+ /**
61
+ * Account selector for `transport: session` intents. Tells the frontend
62
+ * which `CommsAccount` should service this intent on this channel.
63
+ * - `protocol` only: pick the first registered account with this protocol
64
+ * (acceptable when there is one UA per provider per tenant)
65
+ * - `accountId` set: pin to a specific account (multi-trunk / multi-tenant)
66
+ */
67
+ export interface AccountSelector {
68
+ protocol: string;
69
+ accountId?: string;
70
+ }
71
+ export type TransportConfig = {
72
+ kind: "compose";
73
+ /** Optional federated composer resource as "<app>:<resource>". */
74
+ composer?: string;
75
+ /**
76
+ * When true, comms-app `interaction/compose` skips its auto-create
77
+ * and lets the provider own interaction lifecycle (e.g. Teams chat
78
+ * upserts on a chatId externalId).
79
+ */
80
+ ownsInteraction?: boolean;
81
+ } | {
82
+ kind: "session";
83
+ accountSelector: AccountSelector;
84
+ ui: {
85
+ canvas: `${string}:${string}`;
86
+ actionTray?: `${string}:${string}`;
87
+ };
88
+ };
89
+ /**
90
+ * One user-facing intent a provider/channel can perform on a target.
91
+ * `intent` is an open string — known values (call/mail/message/video) get
92
+ * default UI/labels; new ones (`schedule`, `transfer`, `record`) render as
93
+ * generic actions until a provider declares specialised UI.
94
+ */
95
+ export interface IntentCapability {
96
+ intent: string;
97
+ targetSchemes: CommunicationScheme[];
98
+ transport: TransportConfig;
99
+ }
100
+ /**
101
+ * Provider-level feature flag (no routing relevance). Used for things
102
+ * like `call_hold`, `call_forward`, `message_reactions` that providers
103
+ * advertise to feature-detect, but which are not user-pickable intents.
104
+ */
105
+ export interface ProviderFeature {
106
+ name: string;
107
+ scheme?: CommunicationScheme;
108
+ value: boolean;
109
+ }
64
110
  export interface ProviderDescription {
65
111
  id?: string;
66
112
  displayName: string;
67
- capabilities: Capability[];
68
- supportedSchemes: CommunicationScheme[];
113
+ /** User-intents this provider offers by default. Channels inherit. */
114
+ intents: IntentCapability[];
115
+ /** Provider feature flags — separate from routing. */
116
+ features: ProviderFeature[];
69
117
  }
70
118
  export interface PersonalAuthOption {
71
119
  id: string;
@@ -16,6 +16,18 @@ export interface ComposePayload {
16
16
  channelUri?: Uri;
17
17
  recipientUri: Uri;
18
18
  subject?: string;
19
+ /**
20
+ * The user-intent picked in the starter / contact buttons. Lets a
21
+ * provider compose-handler dispatch on intent — e.g. a Microsoft
22
+ * mailbox with intent `message` routes to Teams chat, intent `mail`
23
+ * routes to Outlook send-mail. Open string for forward-compat.
24
+ */
25
+ intent?: string;
26
+ /**
27
+ * Channel-specific extras — template_id, attachments, location, etc.
28
+ * Provider compose-handlers may read this; defaults ignore it.
29
+ */
30
+ extra?: Record<string, any>;
19
31
  }
20
32
  export interface ComposeResponse {
21
33
  messageId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/domain",
3
- "version": "1.17.0",
3
+ "version": "1.18.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",