@opencxh/domain 1.92.0 → 1.94.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.
@@ -0,0 +1,58 @@
1
+ import { OwnerScope } from '../contact/types';
2
+ import { DimensionId } from './dimensions';
3
+ import { WidgetConfig } from './report';
4
+ /**
5
+ * A dashboard SECTION, declaratively. The analytics frontend runs the right widget(s) for the viz and
6
+ * renders the matching component. `title` and the parent def's `title` are i18n keys resolved against the
7
+ * merged locale bundle the owning app ships (see AnalyticsSourceDescription.locales).
8
+ */
9
+ export type SectionViz = "kpis" | "line" | "bar" | "heatmap" | "table";
10
+ export interface DashboardSection {
11
+ viz: SectionViz;
12
+ /** i18n key (namespaced per owning app, e.g. "comms.dash.busiest_times"). */
13
+ title?: string;
14
+ /** kpis: one card per id; line: one series per id; bar/heatmap: switchable set; table: columns. */
15
+ metricIds: string[];
16
+ /** Row dimension for bar/table (line/heatmap imply the time axis). */
17
+ groupBy?: DimensionId;
18
+ }
19
+ /**
20
+ * A preset dashboard shipped by an owning app (or by analytics for cross-source ones). Discovered like
21
+ * metrics/dimensions via /provider/analytics/describe; the frontend composes all sources' dashboards.
22
+ */
23
+ export interface DashboardDef {
24
+ /** Stable, ideally app-namespaced key, e.g. "comms.workload". */
25
+ key: string;
26
+ /** i18n key for the display name (resolved via the source's locale bundle). */
27
+ title: string;
28
+ sections: DashboardSection[];
29
+ /** Owning app (filled by discovery); handy for grouping in the list. */
30
+ source?: string;
31
+ }
32
+ /**
33
+ * A single localized string. Kept as a flat LIST (not a lang->key->value map) because the app-sdk http
34
+ * client mangles object KEYS (snake<->camel) — i18n keys with underscores/dots as map keys get corrupted
35
+ * (e.g. "comms.dash.resolution_over_time"). As list VALUES they pass through untouched.
36
+ */
37
+ export interface LocaleString {
38
+ lang: string;
39
+ key: string;
40
+ value: string;
41
+ }
42
+ export type LocaleBundle = LocaleString[];
43
+ /** Authoring convenience: turn a nested { lang: { key: value } } map into a transport-safe LocaleBundle. */
44
+ export declare function flattenLocales(nested: Record<string, Record<string, string>>): LocaleBundle;
45
+ /**
46
+ * Persisted, user-saved dashboard (future: the custom builder). Distinct from DashboardDef: it stores
47
+ * fully-resolved widget configs rather than a preset template.
48
+ */
49
+ export interface DashboardConfig {
50
+ id: string;
51
+ organizationId: string;
52
+ name: string;
53
+ /** Reuse the platform's ownerScope (org/team/personal). */
54
+ ownerScope?: OwnerScope;
55
+ widgets: WidgetConfig[];
56
+ createdAt?: number;
57
+ updatedAt?: number;
58
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Dimensions — the axes you group or filter reports by.
3
+ *
4
+ * Dimensions are declared/discovered exactly like metrics (see ./metric): there is NO closed enum.
5
+ * Two tiers:
6
+ * - CORE_DIMENSIONS (below): shared and cross-app, with the same key space everywhere
7
+ * (`agent` = userId, `team` = teamId, `channel` = the communication channelId, …). Every source
8
+ * app may emit facts on these. Needed so you can group across sources (comms + voice on `agent`).
9
+ * - App-owned dimensions: declared by the owning app via `GET /analytics/describe`, namespaced
10
+ * (`comms.tag`, `comms.topic`, `comms.cf.<field>`). Only that app emits facts on them.
11
+ *
12
+ * Contract note: a core dimension only merges correctly across sources when every source uses the
13
+ * SAME canonical entity id. An app that cannot guarantee that must declare an app-owned dimension
14
+ * (e.g. `voice.line`) instead of emitting on a core dim.
15
+ */
16
+ /** Dimension id: core (e.g. "agent") or app-namespaced (e.g. "comms.topic"). Open string, not an enum. */
17
+ export type DimensionId = string;
18
+ /** Who handled the work. Core dimension "handledBy"; derived from activity.author.type. NOT separate metrics. */
19
+ export type HandledBy = "teammate" | "autopilot" | "ai_assistant";
20
+ /** How the frontend turns a raw dimension key into a display label. */
21
+ export type DimensionLabelSource = "user" | "team" | "inbox" | "channel" | "topic" | "raw";
22
+ export interface DimensionDefinition {
23
+ id: DimensionId;
24
+ label: string;
25
+ /**
26
+ * Multi-valued: one source record can map to several values (e.g. tags). The rollup then fans out
27
+ * (one fact per value). NB: totals over a multi-valued dim can exceed the conversation count — that
28
+ * is correct/expected; see the multi-valued totals note in docs/ANALYTICS.md §13.
29
+ */
30
+ multiValued?: boolean;
31
+ labelSource?: DimensionLabelSource;
32
+ }
33
+ /**
34
+ * Shared base dimensions — any source app may emit facts on these. Kept un-namespaced on purpose.
35
+ * "time" is special: its value comes from MetricFact.period, not from the dims map.
36
+ */
37
+ export declare const CORE_DIMENSIONS: ({
38
+ id: string;
39
+ label: string;
40
+ labelSource: "user";
41
+ } | {
42
+ id: string;
43
+ label: string;
44
+ labelSource: "team";
45
+ } | {
46
+ id: string;
47
+ label: string;
48
+ labelSource: "inbox";
49
+ } | {
50
+ id: string;
51
+ label: string;
52
+ labelSource: "channel";
53
+ } | {
54
+ id: string;
55
+ label: string;
56
+ labelSource: "raw";
57
+ })[];
@@ -0,0 +1,58 @@
1
+ import { DimensionId } from './dimensions';
2
+ /** Rollup bucket granularity. Coarser grains (week/month) are re-bucketed from these at read time. */
3
+ export type Grain = "hour" | "day";
4
+ /**
5
+ * Open map of dimension-id → value (e.g. { agent: "u_123", channel: "c_9", "comms.topic": "t_4" }).
6
+ * A missing key means that dimension is n/a for this fact. Open map (not a fixed interface) so that a
7
+ * newly declared dimension costs ZERO type changes. "time" is NOT stored here — it comes from `period`.
8
+ */
9
+ export type MetricDimensions = Record<DimensionId, string>;
10
+ /**
11
+ * The measure is an extensible, per-kind shaped payload. This is what makes a non-additive metric
12
+ * (distinct / percentile / min-max) an ADDITIVE extension later: you add a `kind` + a finalize branch
13
+ * without breaking existing facts or the contract. Every kind is bucket-mergeable (additive sums,
14
+ * distinct HLL-unions, distribution sums buckets, extent takes min/max) and snapshot-replaceable (§6).
15
+ *
16
+ * Phase 1 emits & folds only "additive".
17
+ */
18
+ export type MetricMeasure =
19
+ /** sum/avg/rate. count metric: sum = #events; duration/currency: sum = total; count = denominator for avg. */
20
+ {
21
+ kind: "additive";
22
+ sum: number;
23
+ count: number;
24
+ }
25
+ /** distinct counts (e.g. Active accounts) — serialized HyperLogLog sketch. (later) */
26
+ | {
27
+ kind: "distinct";
28
+ hll: string;
29
+ }
30
+ /** histogram/percentile — counts per value bucket, e.g. { "[0,3600000)": 12, "[3600000,)": 30 }. (later) */
31
+ | {
32
+ kind: "distribution";
33
+ buckets: Record<string, number>;
34
+ }
35
+ /** min/max. (later) */
36
+ | {
37
+ kind: "extent";
38
+ min: number;
39
+ max: number;
40
+ };
41
+ /**
42
+ * A MetricFact is the aggregated outcome of one metric, for one period bucket, for one combination of
43
+ * dimension values. It is both what a source app pushes and what analytics stores. It is a declarative
44
+ * SNAPSHOT, not a delta — ingest replaces the measure for a given key (see docs/ANALYTICS.md §5/§6).
45
+ *
46
+ * NB: the stored server entity adds framework-generated `id/createdAt/updatedAt` (not declared in the
47
+ * schema) plus a `source` field so analytics can replace a bucket per source.
48
+ */
49
+ export interface MetricFact {
50
+ organizationId: string;
51
+ metricId: string;
52
+ grain: Grain;
53
+ /** Period key: "2026-07-25T14" (hour) or "2026-07-25" (day). Feeds the "time" dimension. */
54
+ period: string;
55
+ dims: MetricDimensions;
56
+ /** Phase 1 carries only `{ kind: "additive", … }`; the kind follows the metric's aggregation (./metric). */
57
+ measure: MetricMeasure;
58
+ }
@@ -0,0 +1,7 @@
1
+ export * from './dimensions';
2
+ export * from './metric';
3
+ export * from './fact';
4
+ export * from './source';
5
+ export * from './report';
6
+ export * from './dashboard';
7
+ export * from './period';
@@ -0,0 +1,38 @@
1
+ import { DimensionId } from './dimensions';
2
+ /**
3
+ * Metric register — the declarative building block. Fixed in code as a typed `const` in the owning app
4
+ * (no DB), but with exactly this type so we can later move to a DB registry without breaking consumers.
5
+ *
6
+ * `valueType` and `aggregation` are a deliberately BOUNDED central vocabulary (the engine must know how
7
+ * to fold and format). Extending them is a central, additive change — see docs/ANALYTICS.md §13.
8
+ */
9
+ export type MetricValueType = "count" | "duration_ms" | "percent" | "currency";
10
+ export type MetricAggregation = "sum" | "avg" | "rate" | "distinct" | "p50" | "p90" | "min" | "max";
11
+ export interface MetricDefinition {
12
+ /**
13
+ * Globally-unique, app-namespaced id: "<source>.<family>.<name>", e.g. "comms.reply_time.first",
14
+ * "voice.calls.answered". The prefix guarantees disjoint ownership → no double counting across apps.
15
+ */
16
+ id: string;
17
+ /** Human-readable (source language; the UI may localize). */
18
+ label: string;
19
+ /** Picker bucket, e.g. "Volume" | "Responsiviteit" | "Resolutie" | "Taken". */
20
+ category: string;
21
+ /** How a single value is interpreted & formatted. */
22
+ valueType: MetricValueType;
23
+ /** How measures fold across periods and grouped rows. Determines the required measure kind (./fact). */
24
+ aggregation: MetricAggregation;
25
+ /** Only for aggregation:"rate" — ids of the numerator/denominator base metrics (must exist in the catalog). */
26
+ numeratorId?: string;
27
+ denominatorId?: string;
28
+ /**
29
+ * Which dimensions are meaningful to group/filter this metric on.
30
+ * NB: duplicates knowledge held by the extractor — a metric that declares a dim its extractor never
31
+ * emits yields empty groups. Keep them in sync (docs/ANALYTICS.md §13).
32
+ */
33
+ supportedDimensions: DimensionId[];
34
+ /** Optional longer description for tooltips. */
35
+ description?: string;
36
+ /** Hidden from the picker (e.g. a numerator/denominator base metric of a rate). */
37
+ hidden?: boolean;
38
+ }
@@ -0,0 +1,7 @@
1
+ import { Grain } from './fact';
2
+ /** hour -> "YYYY-MM-DDTHH", day -> "YYYY-MM-DD". */
3
+ export declare function formatPeriod(ms: number, grain: Grain): string;
4
+ /** Floor an instant to the start of its bucket (UTC). */
5
+ export declare function floorToPeriod(ms: number, grain: Grain): number;
6
+ /** All period keys from the bucket containing `start` up to and including the bucket of `end`. */
7
+ export declare function periodsInRange(start: number, end: number, grain: Grain): string[];
@@ -0,0 +1,75 @@
1
+ import { DimensionId } from './dimensions';
2
+ import { Grain } from './fact';
3
+ import { MetricDefinition } from './metric';
4
+ /**
5
+ * Report / widget contract (frontend → analytics). One config shape drives tables, counters and the
6
+ * builder. The engine reads only MetricFact rows (never a source store), groups by dims[groupBy], folds
7
+ * the measure and finalizes per the metric's aggregation.
8
+ *
9
+ * The widget/render vocabulary grows centrally by design — genericity lives in data/metrics/dimensions,
10
+ * not in presentation (docs/ANALYTICS.md §13). A future `matrix` widget (rows × cols × 1 metric) covers
11
+ * cross-tabs and heatmaps; distribution widgets cover histograms — both purely additive to this union.
12
+ */
13
+ export type WidgetType = "table" | "counter";
14
+ /**
15
+ * One filter clause. Filters (and cells/totals below) are LISTS, not maps — the app-sdk http client
16
+ * deep-mangles every object KEY (camel<->snake), which would corrupt dimension ids / metric ids used as
17
+ * keys (they contain dots and mixed case). As list VALUES they pass through untouched. This mirrors the
18
+ * platform's existing {name,value}[] pattern (see AIProfile.toolPolicy).
19
+ */
20
+ export interface FilterClause {
21
+ dimensionId: DimensionId;
22
+ value: string;
23
+ }
24
+ export type ReportFilters = FilterClause[];
25
+ /** One metric value in a row/footer. A list entry, not a map key — see FilterClause above. */
26
+ export interface MetricCell {
27
+ metricId: string;
28
+ value: number | null;
29
+ }
30
+ export interface DateRange {
31
+ from: number;
32
+ to: number;
33
+ }
34
+ export interface WidgetConfig {
35
+ type: WidgetType;
36
+ /** counter: exactly 1 metric; table: 1..n columns. */
37
+ metricIds: string[];
38
+ /** Row grouping. Required for table; omit for counter (grand aggregate). "time" → time-series rows. */
39
+ groupBy?: DimensionId;
40
+ filters?: ReportFilters;
41
+ range: DateRange;
42
+ grain: Grain;
43
+ /** counter only: also compute the delta vs. the equally-long previous period. */
44
+ compareToPrevious?: boolean;
45
+ }
46
+ export interface ReportColumn {
47
+ metricId: string;
48
+ /** Sent along so the UI knows valueType/label without a separate lookup. */
49
+ definition: MetricDefinition;
50
+ }
51
+ export interface ReportRow {
52
+ /** Dimension value (agentId/channelId/…) or period key when groupBy = "time". */
53
+ key: string;
54
+ /** One entry per column metric (order matches `columns`); value null = no data. */
55
+ cells: MetricCell[];
56
+ }
57
+ export interface TableResult {
58
+ type: "table";
59
+ groupBy: DimensionId;
60
+ columns: ReportColumn[];
61
+ rows: ReportRow[];
62
+ /** Footer: sum for count metrics, weighted average for avg. Suppress/label for multi-valued groupings. */
63
+ totals: MetricCell[];
64
+ }
65
+ export interface CounterResult {
66
+ type: "counter";
67
+ metricId: string;
68
+ definition: MetricDefinition;
69
+ value: number | null;
70
+ /** Filled when compareToPrevious. */
71
+ previous?: number | null;
72
+ /** (value - previous) / previous. */
73
+ deltaRatio?: number | null;
74
+ }
75
+ export type WidgetResult = TableResult | CounterResult;
@@ -0,0 +1,52 @@
1
+ import { DashboardDef, LocaleBundle } from './dashboard';
2
+ import { DimensionDefinition } from './dimensions';
3
+ import { MetricDefinition } from './metric';
4
+ import { Grain, MetricFact } from './fact';
5
+ /**
6
+ * Source discovery + ingest — the app↔app contract.
7
+ *
8
+ * Each source app declares `app.role({ type: "analytics-source" })` and implements
9
+ * `GET /analytics/describe` → AnalyticsSourceDescription. Analytics enumerates those roles (mirroring
10
+ * the `ai-tool` role auto-discovery) and composes the catalog.
11
+ */
12
+ export interface AnalyticsSourceDescription {
13
+ /** The declaring app's name (== manifest.name == req.source.app). */
14
+ source: string;
15
+ /** Metric definitions this app owns. */
16
+ metrics: MetricDefinition[];
17
+ /** App-owned dimensions this app owns (namespaced). Do NOT repeat core dimensions. */
18
+ dimensions: DimensionDefinition[];
19
+ /** Preset dashboards this app ships (single-source). Titles are i18n keys resolved via `locales`. */
20
+ dashboards?: DashboardDef[];
21
+ /** Locale bundle for this app's dashboard/section title keys (lang -> key -> string). */
22
+ locales?: LocaleBundle;
23
+ }
24
+ /** The composed catalog analytics serves to the frontend (`GET /analytics/metrics`). */
25
+ export interface MetricCatalog {
26
+ /** Merged across all sources. */
27
+ metrics: MetricDefinition[];
28
+ /** CORE_DIMENSIONS + every app-declared dimension. */
29
+ dimensions: DimensionDefinition[];
30
+ }
31
+ /** The composed dashboard catalog analytics serves to the frontend (`GET /dashboards`). */
32
+ export interface DashboardCatalog {
33
+ /** All sources' preset dashboards (+ analytics' own cross-source ones), in discovery order. */
34
+ dashboards: DashboardDef[];
35
+ /** Merged locale bundle across all sources, for resolving dashboard/section title keys. */
36
+ locales: LocaleBundle;
37
+ }
38
+ /**
39
+ * A source app's rollup cron pushes its facts to `POST http://analytics/facts/ingest` (app-to-app auth,
40
+ * org via Bridge.tenant.id). Facts are snapshots: analytics replaces every existing fact within each
41
+ * (source, grain, period) by the pushed set, so a disappeared dimension leaves no orphan rows.
42
+ */
43
+ export interface FactIngestRequest {
44
+ /** Caller org (== Bridge.tenant.id); included for validation. */
45
+ organizationId: string;
46
+ /** Declaring app (== req.source.app). */
47
+ source: string;
48
+ grain: Grain;
49
+ /** The period buckets this push fully covers — analytics replaces within each of these. */
50
+ periods: string[];
51
+ facts: MetricFact[];
52
+ }
@@ -93,6 +93,12 @@ export interface Interaction {
93
93
  updatedAt?: number;
94
94
  lastActivityAt?: number;
95
95
  lastActivityPreview?: ActivityPreview;
96
+ /**
97
+ * Epoch-ms van de eerste uitgaande reactie op deze interactie. Eenmalig gezet door
98
+ * `applyActivityToInteraction` bij de eerste outbound-activity; drijft de first-response-time
99
+ * analytics-metric (firstResponseAt - createdAt). Afwezig = nog geen reactie.
100
+ */
101
+ firstResponseAt?: number;
96
102
  /**
97
103
  * Provider-side folder waar deze thread onder gearchiveerd staat (mail).
98
104
  * Gezet door de sync (folder-membership van already-engaged threads) en door
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=140;function p(e){return e.replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<[^>]+>/g," ").replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/\s+/g," ").trim()}function T(e){const t=e.trim();return t.length<=l?t:t.slice(0,l-1).trimEnd()+"…"}function E(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 f(e){switch(e.type){case"EMAIL_RECEIVED":case"EMAIL_SENT":{const t=e.payload,n=t.bodySnippet?.trim()||p(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?` (${E(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?` (${E(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"AI_ACTION_PROPOSED":return`Voorstel wacht op goedkeuring (${e.payload.actions?.length??0} actie(s))`;case"PLAYBOOK_STARTED":return`Playbook gestart${e.payload.playbookName?`: ${e.payload.playbookName}`:""}`;case"PLAYBOOK_COMPLETED":return"Playbook afgerond";case"PLAYBOOK_ESCALATED":return"Playbook geëscaleerd naar een mens";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?` (${E(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 S(e){return{activityId:e.id,type:e.type,snippet:T(f(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function N(e,t,n=[]){const r=e.createdAt;if(!r)return[];const s=new Set(n);return e.author.type==="user"&&e.author.id&&s.add(e.author.id),Object.entries(t).filter(([a,o])=>o>=r&&!s.has(a)).map(([a])=>a)}const D=new Set(["mail","message"]);function d(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function O(e,t,n,r="html"){if(!e)return n;const s=d(e,t);return s?`${n}${r==="text"?`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const E=140;function p(e){return e.replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<[^>]+>/g," ").replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/\s+/g," ").trim()}function b(e){const t=e.trim();return t.length<=E?t:t.slice(0,E-1).trimEnd()+"…"}function l(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 D(e){switch(e.type){case"EMAIL_RECEIVED":case"EMAIL_SENT":{const t=e.payload,n=t.bodySnippet?.trim()||p(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?` (${l(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?` (${l(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"AI_ACTION_PROPOSED":return`Voorstel wacht op goedkeuring (${e.payload.actions?.length??0} actie(s))`;case"PLAYBOOK_STARTED":return`Playbook gestart${e.payload.playbookName?`: ${e.payload.playbookName}`:""}`;case"PLAYBOOK_COMPLETED":return"Playbook afgerond";case"PLAYBOOK_ESCALATED":return"Playbook geëscaleerd naar een mens";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?` (${l(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 N(e){return{activityId:e.id,type:e.type,snippet:b(D(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function O(e,t,n=[]){const r=e.createdAt;if(!r)return[];const a=new Set(n);return e.author.type==="user"&&e.author.id&&a.add(e.author.id),Object.entries(t).filter(([s,o])=>o>=r&&!a.has(s)).map(([s])=>s)}const R=[{id:"agent",label:"Agent",labelSource:"user"},{id:"team",label:"Team",labelSource:"team"},{id:"inbox",label:"Inbox",labelSource:"inbox"},{id:"channel",label:"Kanaal",labelSource:"channel"},{id:"provider",label:"Provider",labelSource:"raw"},{id:"status",label:"Status",labelSource:"raw"},{id:"handledBy",label:"Afhandelaar",labelSource:"raw"},{id:"time",label:"Tijd",labelSource:"raw"}];function C(e){const t=[];for(const n of Object.keys(e))for(const r of Object.keys(e[n]))t.push({lang:n,key:r,value:e[n][r]});return t}function i(e){return e<10?`0${e}`:`${e}`}function A(e,t){const n=new Date(e),r=`${n.getUTCFullYear()}-${i(n.getUTCMonth()+1)}-${i(n.getUTCDate())}`;return t==="day"?r:`${r}T${i(n.getUTCHours())}`}function g(e,t){const n=new Date(e);return n.setUTCMinutes(0,0,0),t==="day"&&n.setUTCHours(0),n.getTime()}const L=36e5,M=864e5;function m(e,t,n){const r=n==="hour"?L:M,a=[];for(let s=g(e,n);s<=t;s+=r)a.push(A(s,n));return a}const P=new Set(["mail","message"]);function T(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function y(e,t,n,r="html"){if(!e)return n;const a=T(e,t);return a?`${n}${r==="text"?`
2
2
 
3
- `:t==="mail"?"<br><br>-- <br>":"<br><br>"}${s.body}`:n}function R(e){return e.endpoints??[]}const L=e=>!!e.assignedUserId||!!e.assignedInboxId,C=e=>e.status==="closed",b=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,M=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function m(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}const P=[/<blockquote/i,/class="?gmail_quote/i,/id="?[^"]*divRplyFwdMsg/i,/-{3,}\s*Original Message\s*-{3,}/i,/\n_{5,}\s*\n/,/\bOn\b[\s\S]{0,200}?\bwrote:/i];function i(e){let t=e;return t=t.replace(/<(script|style)[\s\S]*?<\/\1>/gi,""),t=t.replace(/<br\s*\/?>/gi,`
3
+ `:t==="mail"?"<br><br>-- <br>":"<br><br>"}${a.body}`:n}function h(e){return e.endpoints??[]}const U=e=>!!e.assignedUserId||!!e.assignedInboxId,$=e=>e.status==="closed",G=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,H=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function V(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}const F=[/<blockquote/i,/class="?gmail_quote/i,/id="?[^"]*divRplyFwdMsg/i,/-{3,}\s*Original Message\s*-{3,}/i,/\n_{5,}\s*\n/,/\bOn\b[\s\S]{0,200}?\bwrote:/i];function c(e){let t=e;return t=t.replace(/<(script|style)[\s\S]*?<\/\1>/gi,""),t=t.replace(/<br\s*\/?>/gi,`
4
4
  `),t=t.replace(/<\/(p|div|li|tr|h[1-6]|ul|ol|table)>/gi,`
5
- `),t=t.replace(/<[^>]+>/g,""),t=t.replace(/<[^>]*$/,""),t}function c(e){return e.replace(/&nbsp;/gi," ").replace(/&lt;/gi,"<").replace(/&gt;/gi,">").replace(/&quot;/gi,'"').replace(/&#39;/gi,"'").replace(/&#(\d+);/g,(t,n)=>String.fromCharCode(Number(n))).replace(/&amp;/gi,"&")}function u(e){return e.replace(/\r/g,"").replace(/[ \t]+/g," ").replace(/ *\n */g,`
5
+ `),t=t.replace(/<[^>]+>/g,""),t=t.replace(/<[^>]*$/,""),t}function u(e){return e.replace(/&nbsp;/gi," ").replace(/&lt;/gi,"<").replace(/&gt;/gi,">").replace(/&quot;/gi,'"').replace(/&#39;/gi,"'").replace(/&#(\d+);/g,(t,n)=>String.fromCharCode(Number(n))).replace(/&amp;/gi,"&")}function d(e){return e.replace(/\r/g,"").replace(/[ \t]+/g," ").replace(/ *\n */g,`
6
6
  `).replace(/\n{3,}/g,`
7
7
 
8
- `).trim()}function y(e){if(typeof e!="string"||!e)return"";let t=e.length;for(const s of P){const a=e.search(s);a>=0&&a<t&&(t=a)}const n=e.slice(0,t);let r=u(c(i(n)));return r||(r=u(c(i(e)))),r}function A(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},s=t.intents.filter(o=>!n.has(o.intent)).map(o=>U(o,r[o.intent]));if(!e.extraIntents||e.extraIntents.length===0)return s;const a=new Set(s.map(o=>o.intent));for(const o of e.extraIntents)a.has(o.intent)||(s.push(o),a.add(o.intent));return s}function g(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 h(e,t){const n=g(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function U(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function G(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const s=t[r.providerId];if(s)for(const a of A(r,s))n.push({channel:r,description:s,capability:a})}return n}function V(e,t){return t.filter(n=>n.capability.intent===e)}function I(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function $(e,t){return I(e.scheme,t)}function H(e,t,n){const r=[];for(const s of e)for(const a of n)a.capability.intent===t&&a.capability.targetSchemes.includes(s.scheme)&&r.push({channelIntent:a,endpoint:s});return r}var _=(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))(_||{});const F="message_window",k="message_templates",B={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},x=(e,t)=>({intent:e,...t}),w="folder_management",W="remote_search",j={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},K=9e4,q="installation-id";exports.CommunicationScheme=_;exports.FEATURE_FOLDER_MANAGEMENT=w;exports.FEATURE_REMOTE_SEARCH=W;exports.INSTALLATION_HEADER=q;exports.InteractionParticipantRole=B;exports.PRESENCE_ONLINE_WINDOW_MS=K;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=k;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=F;exports.SIGNATURE_INTENTS=D;exports.UNSUPPORTED=j;exports.applySignature=O;exports.buildActivityPreview=S;exports.buildChannelIntents=G;exports.cleanMessageText=y;exports.defineIntent=x;exports.disabledIntentsFromCapabilities=h;exports.filterByEndpoint=$;exports.filterByIntent=V;exports.filterByTargetScheme=I;exports.getActivitySeenUserIds=N;exports.getContactEndpoints=R;exports.getShortTitle=M;exports.getUrgencyScore=b;exports.isAssigned=L;exports.isClosed=C;exports.isInteractionUnseen=m;exports.matchContactToIntents=H;exports.resolveAccountCapabilities=g;exports.resolveChannelIntents=A;exports.resolveSignature=d;exports.stripHtml=p;
8
+ `).trim()}function k(e){if(typeof e!="string"||!e)return"";let t=e.length;for(const a of F){const s=e.search(a);s>=0&&s<t&&(t=s)}const n=e.slice(0,t);let r=d(u(c(n)));return r||(r=d(u(c(e)))),r}function I(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},a=t.intents.filter(o=>!n.has(o.intent)).map(o=>x(o,r[o.intent]));if(!e.extraIntents||e.extraIntents.length===0)return a;const s=new Set(a.map(o=>o.intent));for(const o of e.extraIntents)s.has(o.intent)||(a.push(o),s.add(o.intent));return a}function _(e,t){const n={};for(const r of e.intents){if(!r.togglable)continue;const a=t?.[r.intent];n[r.intent]=a??r.defaultEnabled??!0}return n}function w(e,t){const n=_(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function x(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function B(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const a=t[r.providerId];if(a)for(const s of I(r,a))n.push({channel:r,description:a,capability:s})}return n}function W(e,t){return t.filter(n=>n.capability.intent===e)}function f(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function j(e,t){return f(e.scheme,t)}function K(e,t,n){const r=[];for(const a of e)for(const s of n)s.capability.intent===t&&s.capability.targetSchemes.includes(a.scheme)&&r.push({channelIntent:s,endpoint:a});return r}var S=(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))(S||{});const Y="message_window",q="message_templates",z={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},J=(e,t)=>({intent:e,...t}),X="folder_management",Q="remote_search",Z={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},v=9e4,ee="installation-id";exports.CORE_DIMENSIONS=R;exports.CommunicationScheme=S;exports.FEATURE_FOLDER_MANAGEMENT=X;exports.FEATURE_REMOTE_SEARCH=Q;exports.INSTALLATION_HEADER=ee;exports.InteractionParticipantRole=z;exports.PRESENCE_ONLINE_WINDOW_MS=v;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=q;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=Y;exports.SIGNATURE_INTENTS=P;exports.UNSUPPORTED=Z;exports.applySignature=y;exports.buildActivityPreview=N;exports.buildChannelIntents=B;exports.cleanMessageText=k;exports.defineIntent=J;exports.disabledIntentsFromCapabilities=w;exports.filterByEndpoint=j;exports.filterByIntent=W;exports.filterByTargetScheme=f;exports.flattenLocales=C;exports.floorToPeriod=g;exports.formatPeriod=A;exports.getActivitySeenUserIds=O;exports.getContactEndpoints=h;exports.getShortTitle=H;exports.getUrgencyScore=G;exports.isAssigned=U;exports.isClosed=$;exports.isInteractionUnseen=V;exports.matchContactToIntents=K;exports.periodsInRange=m;exports.resolveAccountCapabilities=_;exports.resolveChannelIntents=I;exports.resolveSignature=T;exports.stripHtml=p;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './entities/activity';
2
+ export * from './entities/analytics';
2
3
  export * from './entities/playbook';
3
4
  export * from './entities/kb';
4
5
  export * from './entities/topic';
package/dist/index.js CHANGED
@@ -1,21 +1,21 @@
1
1
  function p(e) {
2
2
  return e.replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/\s+/g, " ").trim();
3
3
  }
4
- function i(e) {
4
+ function d(e) {
5
5
  const t = e.trim();
6
6
  return t.length <= 140 ? t : t.slice(0, 139).trimEnd() + "…";
7
7
  }
8
8
  function l(e) {
9
9
  if (!e || e < 0) return "";
10
- const t = Math.floor(e / 60), r = Math.floor(e % 60);
11
- return `${t}:${r.toString().padStart(2, "0")}`;
10
+ const t = Math.floor(e / 60), n = Math.floor(e % 60);
11
+ return `${t}:${n.toString().padStart(2, "0")}`;
12
12
  }
13
- function d(e) {
13
+ function A(e) {
14
14
  switch (e.type) {
15
15
  case "EMAIL_RECEIVED":
16
16
  case "EMAIL_SENT": {
17
- const t = e.payload, r = t.bodySnippet?.trim() || p(t.body ?? "");
18
- return t.subject ? `${t.subject} — ${r}` : r;
17
+ const t = e.payload, n = t.bodySnippet?.trim() || p(t.body ?? "");
18
+ return t.subject ? `${t.subject} — ${n}` : n;
19
19
  }
20
20
  case "CHAT_MESSAGE_SENT":
21
21
  case "CHAT_MESSAGE_RECEIVED":
@@ -91,47 +91,82 @@ function d(e) {
91
91
  return "";
92
92
  }
93
93
  }
94
- function S(e) {
94
+ function C(e) {
95
95
  return {
96
96
  activityId: e.id,
97
97
  type: e.type,
98
- snippet: i(d(e)),
98
+ snippet: d(A(e)),
99
99
  authorName: e.author?.name ?? "",
100
100
  direction: e.direction,
101
101
  createdAt: e.createdAt ?? Date.now()
102
102
  };
103
103
  }
104
- function N(e, t, r = []) {
105
- const n = e.createdAt;
106
- if (!n) return [];
107
- const s = new Set(r);
108
- return e.author.type === "user" && e.author.id && s.add(e.author.id), Object.entries(t).filter(([a, o]) => o >= n && !s.has(a)).map(([a]) => a);
104
+ function R(e, t, n = []) {
105
+ const r = e.createdAt;
106
+ if (!r) return [];
107
+ const a = new Set(n);
108
+ return e.author.type === "user" && e.author.id && a.add(e.author.id), Object.entries(t).filter(([s, o]) => o >= r && !a.has(s)).map(([s]) => s);
109
+ }
110
+ const M = [
111
+ { id: "agent", label: "Agent", labelSource: "user" },
112
+ { id: "team", label: "Team", labelSource: "team" },
113
+ { id: "inbox", label: "Inbox", labelSource: "inbox" },
114
+ { id: "channel", label: "Kanaal", labelSource: "channel" },
115
+ { id: "provider", label: "Provider", labelSource: "raw" },
116
+ { id: "status", label: "Status", labelSource: "raw" },
117
+ { id: "handledBy", label: "Afhandelaar", labelSource: "raw" },
118
+ { id: "time", label: "Tijd", labelSource: "raw" }
119
+ ];
120
+ function m(e) {
121
+ const t = [];
122
+ for (const n of Object.keys(e))
123
+ for (const r of Object.keys(e[n]))
124
+ t.push({ lang: n, key: r, value: e[n][r] });
125
+ return t;
126
+ }
127
+ function u(e) {
128
+ return e < 10 ? `0${e}` : `${e}`;
109
129
  }
110
- const L = /* @__PURE__ */ new Set(["mail", "message"]);
111
- function A(e, t) {
112
- const r = e.settings?.signatures?.[t];
113
- return !r || !r.enabled || !r.body || r.body.trim() === "" ? null : r;
130
+ function g(e, t) {
131
+ const n = new Date(e), r = `${n.getUTCFullYear()}-${u(n.getUTCMonth() + 1)}-${u(n.getUTCDate())}`;
132
+ return t === "day" ? r : `${r}T${u(n.getUTCHours())}`;
114
133
  }
115
- function O(e, t, r, n = "html") {
116
- if (!e) return r;
117
- const s = A(e, t);
118
- return s ? `${r}${n === "text" ? `
134
+ function T(e, t) {
135
+ const n = new Date(e);
136
+ return n.setUTCMinutes(0, 0, 0), t === "day" && n.setUTCHours(0), n.getTime();
137
+ }
138
+ const f = 36e5, _ = 864e5;
139
+ function P(e, t, n) {
140
+ const r = n === "hour" ? f : _, a = [];
141
+ for (let s = T(e, n); s <= t; s += r)
142
+ a.push(g(s, n));
143
+ return a;
144
+ }
145
+ const $ = /* @__PURE__ */ new Set(["mail", "message"]);
146
+ function I(e, t) {
147
+ const n = e.settings?.signatures?.[t];
148
+ return !n || !n.enabled || !n.body || n.body.trim() === "" ? null : n;
149
+ }
150
+ function h(e, t, n, r = "html") {
151
+ if (!e) return n;
152
+ const a = I(e, t);
153
+ return a ? `${n}${r === "text" ? `
119
154
 
120
- ` : t === "mail" ? "<br><br>-- <br>" : "<br><br>"}${s.body}` : r;
155
+ ` : t === "mail" ? "<br><br>-- <br>" : "<br><br>"}${a.body}` : n;
121
156
  }
122
- function R(e) {
157
+ function y(e) {
123
158
  return e.endpoints ?? [];
124
159
  }
125
- const b = (e) => !!e.assignedUserId || !!e.assignedInboxId, C = (e) => e.status === "closed", M = (e) => ({
160
+ const U = (e) => !!e.assignedUserId || !!e.assignedInboxId, H = (e) => e.status === "closed", V = (e) => ({
126
161
  urgent: 10,
127
162
  high: 5,
128
163
  normal: 2,
129
164
  low: 1
130
- })[e.priority] || 0, m = (e, t = 30) => e.title?.length <= t ? e.title : `${e.title.substring(0, t)}...`;
131
- function P(e, t) {
165
+ })[e.priority] || 0, G = (e, t = 30) => e.title?.length <= t ? e.title : `${e.title.substring(0, t)}...`;
166
+ function k(e, t) {
132
167
  return e.lastActivityAt ? !(e.seenBy ?? []).includes(t) : !1;
133
168
  }
134
- const g = [
169
+ const S = [
135
170
  /<blockquote/i,
136
171
  /class="?gmail_quote/i,
137
172
  // Gmail quote container
@@ -143,89 +178,89 @@ const g = [
143
178
  /\bOn\b[\s\S]{0,200}?\bwrote:/i
144
179
  // Gmail "On <date> <name> wrote:"
145
180
  ];
146
- function E(e) {
181
+ function c(e) {
147
182
  let t = e;
148
183
  return t = t.replace(/<(script|style)[\s\S]*?<\/\1>/gi, ""), t = t.replace(/<br\s*\/?>/gi, `
149
184
  `), t = t.replace(/<\/(p|div|li|tr|h[1-6]|ul|ol|table)>/gi, `
150
185
  `), t = t.replace(/<[^>]+>/g, ""), t = t.replace(/<[^>]*$/, ""), t;
151
186
  }
152
- function u(e) {
153
- return e.replace(/&nbsp;/gi, " ").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;/gi, "'").replace(/&#(\d+);/g, (t, r) => String.fromCharCode(Number(r))).replace(/&amp;/gi, "&");
187
+ function E(e) {
188
+ return e.replace(/&nbsp;/gi, " ").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;/gi, "'").replace(/&#(\d+);/g, (t, n) => String.fromCharCode(Number(n))).replace(/&amp;/gi, "&");
154
189
  }
155
- function c(e) {
190
+ function i(e) {
156
191
  return e.replace(/\r/g, "").replace(/[ \t]+/g, " ").replace(/ *\n */g, `
157
192
  `).replace(/\n{3,}/g, `
158
193
 
159
194
  `).trim();
160
195
  }
161
- function $(e) {
196
+ function w(e) {
162
197
  if (typeof e != "string" || !e) return "";
163
198
  let t = e.length;
164
- for (const s of g) {
165
- const a = e.search(s);
166
- a >= 0 && a < t && (t = a);
199
+ for (const a of S) {
200
+ const s = e.search(a);
201
+ s >= 0 && s < t && (t = s);
167
202
  }
168
- const r = e.slice(0, t);
169
- let n = c(u(E(r)));
170
- return n || (n = c(u(E(e)))), n;
203
+ const n = e.slice(0, t);
204
+ let r = i(E(c(n)));
205
+ return r || (r = i(E(c(e)))), r;
171
206
  }
172
- function _(e, t) {
173
- const r = new Set(e.disabledIntents ?? []), n = e.intentOverrides ?? {}, s = t.intents.filter((o) => !r.has(o.intent)).map((o) => T(o, n[o.intent]));
174
- if (!e.extraIntents || e.extraIntents.length === 0) return s;
175
- const a = new Set(s.map((o) => o.intent));
207
+ function b(e, t) {
208
+ const n = new Set(e.disabledIntents ?? []), r = e.intentOverrides ?? {}, a = t.intents.filter((o) => !n.has(o.intent)).map((o) => O(o, r[o.intent]));
209
+ if (!e.extraIntents || e.extraIntents.length === 0) return a;
210
+ const s = new Set(a.map((o) => o.intent));
176
211
  for (const o of e.extraIntents)
177
- a.has(o.intent) || (s.push(o), a.add(o.intent));
178
- return s;
212
+ s.has(o.intent) || (a.push(o), s.add(o.intent));
213
+ return a;
179
214
  }
180
- function I(e, t) {
181
- const r = {};
182
- for (const n of e.intents) {
183
- if (!n.togglable) continue;
184
- const s = t?.[n.intent];
185
- r[n.intent] = s ?? n.defaultEnabled ?? !0;
215
+ function D(e, t) {
216
+ const n = {};
217
+ for (const r of e.intents) {
218
+ if (!r.togglable) continue;
219
+ const a = t?.[r.intent];
220
+ n[r.intent] = a ?? r.defaultEnabled ?? !0;
186
221
  }
187
- return r;
222
+ return n;
188
223
  }
189
- function h(e, t) {
190
- const r = I(e, t);
191
- return Object.entries(r).filter(([, n]) => !n).map(([n]) => n);
224
+ function x(e, t) {
225
+ const n = D(e, t);
226
+ return Object.entries(n).filter(([, r]) => !r).map(([r]) => r);
192
227
  }
193
- function T(e, t) {
228
+ function O(e, t) {
194
229
  return t ? {
195
230
  intent: t.intent ?? e.intent,
196
231
  targetSchemes: t.targetSchemes ?? e.targetSchemes,
197
232
  transport: t.transport ?? e.transport
198
233
  } : e;
199
234
  }
200
- function y(e, t) {
201
- const r = [];
202
- for (const n of e) {
203
- if (!n.enabled) continue;
204
- const s = t[n.providerId];
205
- if (s)
206
- for (const a of _(n, s))
207
- r.push({ channel: n, description: s, capability: a });
235
+ function F(e, t) {
236
+ const n = [];
237
+ for (const r of e) {
238
+ if (!r.enabled) continue;
239
+ const a = t[r.providerId];
240
+ if (a)
241
+ for (const s of b(r, a))
242
+ n.push({ channel: r, description: a, capability: s });
208
243
  }
209
- return r;
244
+ return n;
210
245
  }
211
- function V(e, t) {
212
- return t.filter((r) => r.capability.intent === e);
246
+ function B(e, t) {
247
+ return t.filter((n) => n.capability.intent === e);
213
248
  }
214
- function f(e, t) {
215
- return t.filter((r) => r.capability.targetSchemes.includes(e));
249
+ function N(e, t) {
250
+ return t.filter((n) => n.capability.targetSchemes.includes(e));
216
251
  }
217
- function G(e, t) {
218
- return f(e.scheme, t);
252
+ function j(e, t) {
253
+ return N(e.scheme, t);
219
254
  }
220
- function H(e, t, r) {
221
- const n = [];
222
- for (const s of e)
223
- for (const a of r)
224
- a.capability.intent === t && a.capability.targetSchemes.includes(s.scheme) && n.push({ channelIntent: a, endpoint: s });
225
- return n;
255
+ function W(e, t, n) {
256
+ const r = [];
257
+ for (const a of e)
258
+ for (const s of n)
259
+ s.capability.intent === t && s.capability.targetSchemes.includes(a.scheme) && r.push({ channelIntent: s, endpoint: a });
260
+ return r;
226
261
  }
227
- var D = /* @__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.MESSENGER = "messenger", e.INSTAGRAM = "instagram", e.VIBER = "viber", e.SMS = "sms", e.FAX = "fax", e.TEAMS = "teams", e.CALENDAR = "calendar", e))(D || {});
228
- const U = "message_window", k = "message_templates", x = {
262
+ var L = /* @__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.MESSENGER = "messenger", e.INSTAGRAM = "instagram", e.VIBER = "viber", e.SMS = "sms", e.FAX = "fax", e.TEAMS = "teams", e.CALENDAR = "calendar", e))(L || {});
263
+ const K = "message_window", Y = "message_templates", q = {
229
264
  // E-mail
230
265
  FROM: "from",
231
266
  TO: "to",
@@ -242,37 +277,42 @@ const U = "message_window", k = "message_templates", x = {
242
277
  // Voice/conference
243
278
  HOST: "host",
244
279
  PARTICIPANT: "participant"
245
- }, F = (e, t) => ({ intent: e, ...t }), w = "folder_management", B = "remote_search", W = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, j = 9e4, K = "installation-id";
280
+ }, X = (e, t) => ({ intent: e, ...t }), z = "folder_management", J = "remote_search", Q = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, Z = 9e4, v = "installation-id";
246
281
  export {
247
- D as CommunicationScheme,
248
- w as FEATURE_FOLDER_MANAGEMENT,
249
- B as FEATURE_REMOTE_SEARCH,
250
- K as INSTALLATION_HEADER,
251
- x as InteractionParticipantRole,
252
- j as PRESENCE_ONLINE_WINDOW_MS,
253
- k as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
254
- U as PROVIDER_FEATURE_MESSAGE_WINDOW,
255
- L as SIGNATURE_INTENTS,
256
- W as UNSUPPORTED,
257
- O as applySignature,
258
- S as buildActivityPreview,
259
- y as buildChannelIntents,
260
- $ as cleanMessageText,
261
- F as defineIntent,
262
- h as disabledIntentsFromCapabilities,
263
- G as filterByEndpoint,
264
- V as filterByIntent,
265
- f as filterByTargetScheme,
266
- N as getActivitySeenUserIds,
267
- R as getContactEndpoints,
268
- m as getShortTitle,
269
- M as getUrgencyScore,
270
- b as isAssigned,
271
- C as isClosed,
272
- P as isInteractionUnseen,
273
- H as matchContactToIntents,
274
- I as resolveAccountCapabilities,
275
- _ as resolveChannelIntents,
276
- A as resolveSignature,
282
+ M as CORE_DIMENSIONS,
283
+ L as CommunicationScheme,
284
+ z as FEATURE_FOLDER_MANAGEMENT,
285
+ J as FEATURE_REMOTE_SEARCH,
286
+ v as INSTALLATION_HEADER,
287
+ q as InteractionParticipantRole,
288
+ Z as PRESENCE_ONLINE_WINDOW_MS,
289
+ Y as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
290
+ K as PROVIDER_FEATURE_MESSAGE_WINDOW,
291
+ $ as SIGNATURE_INTENTS,
292
+ Q as UNSUPPORTED,
293
+ h as applySignature,
294
+ C as buildActivityPreview,
295
+ F as buildChannelIntents,
296
+ w as cleanMessageText,
297
+ X as defineIntent,
298
+ x as disabledIntentsFromCapabilities,
299
+ j as filterByEndpoint,
300
+ B as filterByIntent,
301
+ N as filterByTargetScheme,
302
+ m as flattenLocales,
303
+ T as floorToPeriod,
304
+ g as formatPeriod,
305
+ R as getActivitySeenUserIds,
306
+ y as getContactEndpoints,
307
+ G as getShortTitle,
308
+ V as getUrgencyScore,
309
+ U as isAssigned,
310
+ H as isClosed,
311
+ k as isInteractionUnseen,
312
+ W as matchContactToIntents,
313
+ P as periodsInRange,
314
+ D as resolveAccountCapabilities,
315
+ b as resolveChannelIntents,
316
+ I as resolveSignature,
277
317
  p as stripHtml
278
318
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/domain",
3
- "version": "1.92.0",
3
+ "version": "1.94.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",