@whitewall/blip-sdk 0.0.179 → 0.0.181
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/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/namespaces/desk.js +64 -1
- package/dist/cjs/namespaces/desk.js.map +1 -1
- package/dist/cjs/sender/enveloperesolver.js +2 -2
- package/dist/cjs/sender/enveloperesolver.js.map +1 -1
- package/dist/cjs/sender/sender.js +6 -0
- package/dist/cjs/sender/sender.js.map +1 -1
- package/dist/cjs/sender/sessionnegotiator.js +15 -7
- package/dist/cjs/sender/sessionnegotiator.js.map +1 -1
- package/dist/cjs/sender/tcp/tcpsender.js +47 -31
- package/dist/cjs/sender/tcp/tcpsender.js.map +1 -1
- package/dist/cjs/sender/websocket/websocketsender.js +40 -12
- package/dist/cjs/sender/websocket/websocketsender.js.map +1 -1
- package/dist/cjs/utils/builder.js +121 -0
- package/dist/cjs/utils/builder.js.map +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/namespaces/desk.js +64 -1
- package/dist/esm/namespaces/desk.js.map +1 -1
- package/dist/esm/sender/enveloperesolver.js +2 -2
- package/dist/esm/sender/enveloperesolver.js.map +1 -1
- package/dist/esm/sender/sender.js +6 -0
- package/dist/esm/sender/sender.js.map +1 -1
- package/dist/esm/sender/sessionnegotiator.js +15 -7
- package/dist/esm/sender/sessionnegotiator.js.map +1 -1
- package/dist/esm/sender/tcp/tcpsender.js +47 -31
- package/dist/esm/sender/tcp/tcpsender.js.map +1 -1
- package/dist/esm/sender/websocket/websocketsender.js +40 -12
- package/dist/esm/sender/websocket/websocketsender.js.map +1 -1
- package/dist/esm/utils/builder.js +115 -0
- package/dist/esm/utils/builder.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/namespaces/desk.d.ts +73 -1
- package/dist/types/namespaces/desk.d.ts.map +1 -1
- package/dist/types/sender/enveloperesolver.d.ts +1 -1
- package/dist/types/sender/enveloperesolver.d.ts.map +1 -1
- package/dist/types/sender/sender.d.ts +1 -0
- package/dist/types/sender/sender.d.ts.map +1 -1
- package/dist/types/sender/sessionnegotiator.d.ts +2 -0
- package/dist/types/sender/sessionnegotiator.d.ts.map +1 -1
- package/dist/types/sender/tcp/tcpsender.d.ts.map +1 -1
- package/dist/types/types/desk.d.ts +18 -0
- package/dist/types/types/desk.d.ts.map +1 -1
- package/dist/types/utils/builder.d.ts +54 -0
- package/dist/types/utils/builder.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/namespaces/desk.ts +145 -1
- package/src/sender/enveloperesolver.ts +2 -2
- package/src/sender/sender.ts +7 -0
- package/src/sender/sessionnegotiator.ts +16 -7
- package/src/sender/tcp/tcpsender.ts +51 -33
- package/src/sender/websocket/websocketsender.ts +40 -14
- package/src/types/desk.ts +19 -0
- package/src/utils/builder.ts +165 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tcpsender.d.ts","sourceRoot":"","sources":["../../../../src/sender/tcp/tcpsender.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACR,OAAO,EACP,cAAc,EAGd,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACzB,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"tcpsender.d.ts","sourceRoot":"","sources":["../../../../src/sender/tcp/tcpsender.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACR,OAAO,EACP,cAAc,EAGd,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACzB,MAAM,sBAAsB,CAAA;AAG7B,OAAO,EAAoB,KAAK,2BAA2B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAMvG;;;;;GAKG;AACH,qBAAa,SAAU,SAAQ,oBAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;gBAE1C,OAAO,EAAE,qBAAqB,CAAC,2BAA2B,CAAC,CAAC,CAAC,CAAC;IAmC7D,gBAAgB,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3D,WAAW,CAAC,IAAI,SAAS,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IASnF,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IA0BzD,mBAAmB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpE,KAAK;IAMlB,OAAc,KAAK;;;MAAoC;YAEzC,eAAe;CAahC"}
|
|
@@ -79,6 +79,24 @@ export type DeskRule = {
|
|
|
79
79
|
storageDate?: string;
|
|
80
80
|
queueId?: string;
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Filters accepted by every `/analytics/reports/*` endpoint. The backend
|
|
84
|
+
* applies all filters with AND semantics; arrays inside a single filter are
|
|
85
|
+
* OR'd together.
|
|
86
|
+
*/
|
|
87
|
+
export type ReportFilters = {
|
|
88
|
+
beginDate: string | Date;
|
|
89
|
+
endDate: string | Date;
|
|
90
|
+
teams?: Array<string>;
|
|
91
|
+
tags?: Array<string>;
|
|
92
|
+
agentIdentities?: Array<Identity>;
|
|
93
|
+
customerIdentities?: Array<Identity>;
|
|
94
|
+
domains?: Array<string>;
|
|
95
|
+
/** Include SLA applied/achieved counts on agent rows. */
|
|
96
|
+
calculateSla?: boolean;
|
|
97
|
+
/** Force a fresh aggregation on `/tags`, bypassing the server cache. */
|
|
98
|
+
refreshCache?: boolean;
|
|
99
|
+
};
|
|
82
100
|
export type DeskPriorityRule = {
|
|
83
101
|
id?: string;
|
|
84
102
|
ownerIdentity?: Identity;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"desk.d.ts","sourceRoot":"","sources":["../../../src/types/desk.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"desk.d.ts","sourceRoot":"","sources":["../../../src/types/desk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Action, ContentActionItem, State } from '../types/flow.ts';
|
|
2
|
+
export type MinifiedAction = {
|
|
3
|
+
type: string;
|
|
4
|
+
settings: Record<string, unknown>;
|
|
5
|
+
conditions?: Array<{
|
|
6
|
+
source: string;
|
|
7
|
+
variable?: string;
|
|
8
|
+
comparison: string;
|
|
9
|
+
operator?: string;
|
|
10
|
+
values: Array<string>;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
export type MinifiedInput = {
|
|
14
|
+
bypass: boolean;
|
|
15
|
+
variable?: string;
|
|
16
|
+
validation?: {
|
|
17
|
+
rule: string;
|
|
18
|
+
regex?: string;
|
|
19
|
+
type?: string;
|
|
20
|
+
error: string;
|
|
21
|
+
};
|
|
22
|
+
expiration?: string;
|
|
23
|
+
conditions?: MinifiedAction['conditions'];
|
|
24
|
+
};
|
|
25
|
+
export type MinifiedOutput = {
|
|
26
|
+
stateId: string;
|
|
27
|
+
stateName?: string;
|
|
28
|
+
typeOfStateId?: string;
|
|
29
|
+
conditions?: MinifiedAction['conditions'];
|
|
30
|
+
};
|
|
31
|
+
export type MinifiedState = {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
root?: boolean;
|
|
35
|
+
enteringActions?: Array<MinifiedAction>;
|
|
36
|
+
contentActions?: Array<{
|
|
37
|
+
action?: MinifiedAction;
|
|
38
|
+
input?: MinifiedInput;
|
|
39
|
+
}>;
|
|
40
|
+
leavingActions?: Array<MinifiedAction>;
|
|
41
|
+
outputs?: Array<MinifiedOutput>;
|
|
42
|
+
defaultOutput: {
|
|
43
|
+
stateId: string;
|
|
44
|
+
stateName?: string;
|
|
45
|
+
typeOfStateId?: string;
|
|
46
|
+
};
|
|
47
|
+
afterStateChangedActions?: Array<MinifiedAction>;
|
|
48
|
+
localActions?: Array<MinifiedAction>;
|
|
49
|
+
};
|
|
50
|
+
export declare function minifyAction(action: Action): MinifiedAction;
|
|
51
|
+
export declare function isChatStateSendMessage(item: ContentActionItem): boolean;
|
|
52
|
+
export declare function resolveStateName(stateId: string, flow: Record<string, State>): string | undefined;
|
|
53
|
+
export declare function minifyState(stateId: string, state: State, flow: Record<string, State>): MinifiedState;
|
|
54
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../src/utils/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAExE,MAAM,MAAM,cAAc,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,UAAU,CAAC,EAAE,KAAK,CAAC;QACf,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,EAAE,MAAM,CAAA;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;KACxB,CAAC,CAAA;CACL,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IACxB,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,cAAc,CAAC,YAAY,CAAC,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,cAAc,CAAC,YAAY,CAAC,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,eAAe,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IACvC,cAAc,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC,CAAA;IAC1E,cAAc,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IACtC,OAAO,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IAC/B,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9E,wBAAwB,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IAChD,YAAY,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;CACvC,CAAA;AA4BD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAQ3D;AAoBD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAEvE;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,GAAG,SAAS,CAGjG;AAkBD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,aAAa,CA2CrG"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './namespaces/namespace.ts'
|
|
|
3
3
|
export * from './schemas/index.ts'
|
|
4
4
|
export * from './sender/index.ts'
|
|
5
5
|
export * from './types/index.ts'
|
|
6
|
+
export * from './utils/builder.ts'
|
|
6
7
|
export * from './utils/desk.ts'
|
|
7
8
|
export * from './utils/odata.ts'
|
|
8
9
|
export * from './utils/thread.ts'
|
package/src/namespaces/desk.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type DetailedAttendanceHour,
|
|
9
9
|
type Identity,
|
|
10
10
|
Node,
|
|
11
|
+
type ReportFilters,
|
|
11
12
|
type ThreadItem,
|
|
12
13
|
type Ticket,
|
|
13
14
|
type TicketStatus,
|
|
@@ -522,8 +523,10 @@ export class DeskNamespace extends Namespace {
|
|
|
522
523
|
}}`,
|
|
523
524
|
},
|
|
524
525
|
{
|
|
525
|
-
|
|
526
|
+
// The server caps $take at 500; use it to minimize round trips when paginating
|
|
527
|
+
take: Math.min(opts?.max ?? 500, 500),
|
|
526
528
|
...opts,
|
|
529
|
+
collection: true,
|
|
527
530
|
},
|
|
528
531
|
)
|
|
529
532
|
}
|
|
@@ -823,6 +826,147 @@ export class DeskNamespace extends Namespace {
|
|
|
823
826
|
)
|
|
824
827
|
}
|
|
825
828
|
|
|
829
|
+
/**
|
|
830
|
+
* Shared filters for the `/analytics/reports/*` endpoints. All reports
|
|
831
|
+
* are SQL-backed pre-aggregations over closed/historical tickets, so
|
|
832
|
+
* they are the cheapest way to answer "group X by Y" questions over a
|
|
833
|
+
* date range without paginating ticket lists.
|
|
834
|
+
*/
|
|
835
|
+
private buildReportQuery(filters: ReportFilters): Record<string, unknown> {
|
|
836
|
+
return {
|
|
837
|
+
beginDate: filters.beginDate,
|
|
838
|
+
endDate: filters.endDate,
|
|
839
|
+
teams: filters.teams?.join(','),
|
|
840
|
+
tags: filters.tags?.join(','),
|
|
841
|
+
agentIdentities: filters.agentIdentities?.join(','),
|
|
842
|
+
customerIdentities: filters.customerIdentities?.join(','),
|
|
843
|
+
domains: filters.domains?.join(','),
|
|
844
|
+
calculateSla: filters.calculateSla,
|
|
845
|
+
refreshCache: filters.refreshCache,
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Returns one row per tag found in tickets closed within the date range,
|
|
851
|
+
* with the number of tickets and average timings. Cached by the backend.
|
|
852
|
+
*/
|
|
853
|
+
public async getTagsReport(
|
|
854
|
+
filters: ReportFilters,
|
|
855
|
+
opts?: ConsumeOptions,
|
|
856
|
+
): Promise<
|
|
857
|
+
Array<{
|
|
858
|
+
name: string
|
|
859
|
+
closedTickets: number
|
|
860
|
+
ticketsCount: number
|
|
861
|
+
/** Format: `hh:mm:ss[.fffffff]` from .NET TimeSpan. */
|
|
862
|
+
averageFirstResponseTime?: string
|
|
863
|
+
averageWaitTime?: string
|
|
864
|
+
averageAttendanceTime?: string
|
|
865
|
+
averageResponseTime?: string
|
|
866
|
+
}>
|
|
867
|
+
> {
|
|
868
|
+
return await this.sendCommand(
|
|
869
|
+
{
|
|
870
|
+
method: 'get',
|
|
871
|
+
uri: uri`/analytics/reports/tags?${this.buildReportQuery(filters)}`,
|
|
872
|
+
},
|
|
873
|
+
{ collection: true, ...opts },
|
|
874
|
+
)
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Returns one row per team for tickets in the date range, with closed
|
|
879
|
+
* counts and average timings.
|
|
880
|
+
*/
|
|
881
|
+
public async getTeamsReport(
|
|
882
|
+
filters: ReportFilters,
|
|
883
|
+
opts?: ConsumeOptions,
|
|
884
|
+
): Promise<
|
|
885
|
+
Array<{
|
|
886
|
+
name: string
|
|
887
|
+
closedTickets: number
|
|
888
|
+
ticketsCount: number
|
|
889
|
+
waitingTickets: number
|
|
890
|
+
openedTickets: number
|
|
891
|
+
averageFirstResponseTime?: string
|
|
892
|
+
averageWaitTime?: string
|
|
893
|
+
averageAttendanceTime?: string
|
|
894
|
+
averageResponseTime?: string
|
|
895
|
+
}>
|
|
896
|
+
> {
|
|
897
|
+
return await this.sendCommand(
|
|
898
|
+
{
|
|
899
|
+
method: 'get',
|
|
900
|
+
uri: uri`/analytics/reports/teams?${this.buildReportQuery(filters)}`,
|
|
901
|
+
},
|
|
902
|
+
{ collection: true, ...opts },
|
|
903
|
+
)
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Returns one row per agent (identified by `identity`) for tickets in the
|
|
908
|
+
* date range, with closed counts, average timings and optional SLA counts
|
|
909
|
+
* when `calculateSla` is true.
|
|
910
|
+
*/
|
|
911
|
+
public async getAttendantsReport(
|
|
912
|
+
filters: ReportFilters,
|
|
913
|
+
opts?: ConsumeOptions,
|
|
914
|
+
): Promise<
|
|
915
|
+
Array<{
|
|
916
|
+
identity: Identity
|
|
917
|
+
agentName?: string
|
|
918
|
+
status?: 'Offline' | 'Pause' | 'Online' | 'Invisible'
|
|
919
|
+
isEnabled?: boolean
|
|
920
|
+
closedTickets: number
|
|
921
|
+
ticketsCount: number
|
|
922
|
+
openedTickets?: number
|
|
923
|
+
slaAppliedTicketCount?: number
|
|
924
|
+
slaAchievedTicketCount?: number
|
|
925
|
+
averageFirstResponseTime?: string
|
|
926
|
+
averageWaitTime?: string
|
|
927
|
+
averageAttendanceTime?: string
|
|
928
|
+
averageResponseTime?: string
|
|
929
|
+
}>
|
|
930
|
+
> {
|
|
931
|
+
return await this.sendCommand(
|
|
932
|
+
{
|
|
933
|
+
method: 'get',
|
|
934
|
+
uri: uri`/analytics/reports/attendants?${this.buildReportQuery(filters)}`,
|
|
935
|
+
},
|
|
936
|
+
{ collection: true, ...opts },
|
|
937
|
+
)
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Returns one row per day in the date range with ticket status counts
|
|
942
|
+
* (open, closed, waiting, transferred, missed, …). Suitable for daily
|
|
943
|
+
* time-series charts.
|
|
944
|
+
*/
|
|
945
|
+
public async getTicketsReport(
|
|
946
|
+
filters: ReportFilters,
|
|
947
|
+
opts?: ConsumeOptions,
|
|
948
|
+
): Promise<
|
|
949
|
+
Array<{
|
|
950
|
+
date: string
|
|
951
|
+
open: number
|
|
952
|
+
waiting: number
|
|
953
|
+
closed: number
|
|
954
|
+
closedAttendant: number
|
|
955
|
+
closedClient: number
|
|
956
|
+
transferred: number
|
|
957
|
+
missed: number
|
|
958
|
+
inAttendance: number
|
|
959
|
+
}>
|
|
960
|
+
> {
|
|
961
|
+
return await this.sendCommand(
|
|
962
|
+
{
|
|
963
|
+
method: 'get',
|
|
964
|
+
uri: uri`/analytics/reports/tickets?${this.buildReportQuery(filters)}`,
|
|
965
|
+
},
|
|
966
|
+
{ collection: true, ...opts },
|
|
967
|
+
)
|
|
968
|
+
}
|
|
969
|
+
|
|
826
970
|
public async getTags(team?: string, opts?: ConsumeOptions): Promise<Array<string>> {
|
|
827
971
|
if (team && team !== 'DIRECT_TRANSFER') {
|
|
828
972
|
const result = await this.sendCommand<'get', Array<{ tag: string }>>(
|
|
@@ -164,9 +164,9 @@ export class EnvelopeResolver {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
public rejectPendingEnvelopes(
|
|
167
|
+
public rejectPendingEnvelopes(error: Error) {
|
|
168
168
|
for (const id in this.waitingEnvelopeResponseResolvers) {
|
|
169
|
-
this.resolveEnvelopeResponse(id,
|
|
169
|
+
this.resolveEnvelopeResponse(id, error)
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
|
package/src/sender/sender.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from '../types/index.ts'
|
|
13
13
|
import type { Closable } from './closable.ts'
|
|
14
14
|
import { EnvelopeResolver, type EventMap, type Listener } from './enveloperesolver.ts'
|
|
15
|
+
import { RetryableError } from './retryableerror.ts'
|
|
15
16
|
import type { Authentication } from './security.ts'
|
|
16
17
|
import type { SessionNegotiator } from './sessionnegotiator.ts'
|
|
17
18
|
|
|
@@ -149,4 +150,10 @@ export abstract class OpenConnectionSender extends ConnectionSender implements S
|
|
|
149
150
|
this.envelopeResolver.close()
|
|
150
151
|
return Promise.resolve()
|
|
151
152
|
}
|
|
153
|
+
|
|
154
|
+
protected rejectPending(reason: string) {
|
|
155
|
+
const error = new RetryableError(reason)
|
|
156
|
+
this.sessionNegotiator?.fail(error)
|
|
157
|
+
this.envelopeResolver.rejectPendingEnvelopes(error)
|
|
158
|
+
}
|
|
152
159
|
}
|
|
@@ -17,6 +17,7 @@ export class SessionNegotiator {
|
|
|
17
17
|
|
|
18
18
|
private presencePromise: Promise<void> | null = null
|
|
19
19
|
private currentSessionResolver: Pick<PromiseWithResolvers<Session>, 'reject' | 'resolve'> | null = null
|
|
20
|
+
private failure: Error | null = null
|
|
20
21
|
|
|
21
22
|
constructor(
|
|
22
23
|
private readonly sender: OpenConnectionSender,
|
|
@@ -28,11 +29,8 @@ export class SessionNegotiator {
|
|
|
28
29
|
authentication: Authentication
|
|
29
30
|
upgradeToTls?: () => Promise<void>
|
|
30
31
|
}): Promise<void> {
|
|
31
|
-
const timeout = setTimeout(
|
|
32
|
-
|
|
33
|
-
await this.sender.close()
|
|
34
|
-
throw new Error('Negotiation timeout')
|
|
35
|
-
}
|
|
32
|
+
const timeout = setTimeout(() => {
|
|
33
|
+
this.fail(new Error('Negotiation timeout'))
|
|
36
34
|
}, 60000) // 60 seconds
|
|
37
35
|
|
|
38
36
|
try {
|
|
@@ -116,6 +114,14 @@ export class SessionNegotiator {
|
|
|
116
114
|
}
|
|
117
115
|
}
|
|
118
116
|
|
|
117
|
+
public fail(error: Error) {
|
|
118
|
+
this.failure = error
|
|
119
|
+
if (this.currentSessionResolver) {
|
|
120
|
+
this.currentSessionResolver.reject(error)
|
|
121
|
+
this.currentSessionResolver = null
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
119
125
|
public async ensurePresence(currentCommandUri = ''): Promise<void> {
|
|
120
126
|
if (!this.session) {
|
|
121
127
|
throw new Error('Session not established')
|
|
@@ -159,11 +165,11 @@ export class SessionNegotiator {
|
|
|
159
165
|
|
|
160
166
|
public finish() {
|
|
161
167
|
if (!this.session) {
|
|
162
|
-
|
|
168
|
+
return
|
|
163
169
|
}
|
|
164
170
|
|
|
165
171
|
this.sendSession({
|
|
166
|
-
id: this.session
|
|
172
|
+
id: this.session.id,
|
|
167
173
|
state: 'finishing',
|
|
168
174
|
})
|
|
169
175
|
}
|
|
@@ -173,6 +179,9 @@ export class SessionNegotiator {
|
|
|
173
179
|
}
|
|
174
180
|
|
|
175
181
|
private async waitForSessionResponse(): Promise<Session> {
|
|
182
|
+
if (this.failure) {
|
|
183
|
+
throw this.failure
|
|
184
|
+
}
|
|
176
185
|
const { promise, resolve, reject } = Promise.withResolvers<Session>()
|
|
177
186
|
this.currentSessionResolver = { resolve, reject }
|
|
178
187
|
const session = await promise
|
|
@@ -10,7 +10,6 @@ import type {
|
|
|
10
10
|
Notification,
|
|
11
11
|
UnknownCommandResponse,
|
|
12
12
|
} from '../../types/index.ts'
|
|
13
|
-
import { logger } from '../../utils/logger.ts'
|
|
14
13
|
import { BlipError } from '../bliperror.ts'
|
|
15
14
|
import { RetryableError } from '../retryableerror.ts'
|
|
16
15
|
import { ConnectionSender, type ConnectionSenderConstructor, OpenConnectionSender } from '../sender.ts'
|
|
@@ -41,21 +40,20 @@ export class TCPSender extends OpenConnectionSender {
|
|
|
41
40
|
this.connectionHandle = new TCPHandle<Envelope>(
|
|
42
41
|
host,
|
|
43
42
|
443,
|
|
44
|
-
() => {
|
|
43
|
+
(socketRef) => {
|
|
45
44
|
this.sessionNegotiator = new SessionNegotiator(this, (session) => {
|
|
46
|
-
|
|
47
|
-
.get()
|
|
48
|
-
.then((s) => s.write(JSON.stringify(session)))
|
|
49
|
-
.catch((err) => logger.warn('TCPSender', 'Failed to write session frame', err))
|
|
45
|
+
socketRef.current.write(JSON.stringify(session))
|
|
50
46
|
})
|
|
51
|
-
const upgradeToTls = tlsOptions
|
|
47
|
+
const upgradeToTls = tlsOptions
|
|
48
|
+
? () => this.connectionHandle.upgradeToTls(tlsOptions, socketRef)
|
|
49
|
+
: undefined
|
|
52
50
|
return this.sessionNegotiator.negotiate({
|
|
53
51
|
node: options.node,
|
|
54
52
|
authentication: auth,
|
|
55
53
|
upgradeToTls,
|
|
56
54
|
})
|
|
57
55
|
},
|
|
58
|
-
() => this.
|
|
56
|
+
() => this.rejectPending('Connection was closed'),
|
|
59
57
|
(envelope: Envelope) => {
|
|
60
58
|
if (this.sessionNegotiator?.negotiating) {
|
|
61
59
|
return this.sessionNegotiator.handleEnvelope(envelope)
|
|
@@ -143,7 +141,7 @@ class TCPHandle<T> {
|
|
|
143
141
|
constructor(
|
|
144
142
|
host: string,
|
|
145
143
|
port: number,
|
|
146
|
-
onConnected: () => Promise<void>,
|
|
144
|
+
onConnected: (socketRef: { current: Socket }) => Promise<void>,
|
|
147
145
|
onClose: () => void,
|
|
148
146
|
onMessage: (message: T) => void,
|
|
149
147
|
) {
|
|
@@ -167,17 +165,19 @@ class TCPHandle<T> {
|
|
|
167
165
|
}
|
|
168
166
|
}
|
|
169
167
|
|
|
170
|
-
public async upgradeToTls(options: ConnectionOptions): Promise<void> {
|
|
171
|
-
|
|
172
|
-
throw new Error('Cannot upgrade: no active socket.')
|
|
173
|
-
}
|
|
174
|
-
const plain = await this.currentSocketPromise
|
|
168
|
+
public async upgradeToTls(options: ConnectionOptions, socketRef: { current: Socket }): Promise<void> {
|
|
169
|
+
const plain = socketRef.current
|
|
175
170
|
plain.removeAllListeners('data')
|
|
176
171
|
this.buffer = Buffer.alloc(0)
|
|
177
172
|
|
|
178
173
|
const { connect: tlsConnect } = await import('node:tls')
|
|
179
174
|
const secured = tlsConnect({ ...options, socket: plain })
|
|
180
175
|
|
|
176
|
+
// Permanent no-op handler attached up front, so errors that arrive in
|
|
177
|
+
// the microtask gap after secureConnect resolves don't become
|
|
178
|
+
// uncaughtException. The underlying TCP socket's 'close' drives reconnect.
|
|
179
|
+
secured.on('error', () => undefined)
|
|
180
|
+
|
|
181
181
|
await new Promise<void>((resolve, reject) => {
|
|
182
182
|
const onError = (err: Error) => {
|
|
183
183
|
secured.off('secureConnect', onConnect)
|
|
@@ -192,7 +192,7 @@ class TCPHandle<T> {
|
|
|
192
192
|
})
|
|
193
193
|
|
|
194
194
|
this.attachDataListener(secured)
|
|
195
|
-
|
|
195
|
+
socketRef.current = secured
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
private attachDataListener(socket: NodeJS.ReadableStream) {
|
|
@@ -209,7 +209,7 @@ class TCPHandle<T> {
|
|
|
209
209
|
private async connect(
|
|
210
210
|
host: string,
|
|
211
211
|
port: number,
|
|
212
|
-
onConnected: () => Promise<void>,
|
|
212
|
+
onConnected: (socketRef: { current: Socket }) => Promise<void>,
|
|
213
213
|
onClose: () => void,
|
|
214
214
|
): Promise<Socket> {
|
|
215
215
|
const { connect } = await import('node:net')
|
|
@@ -217,42 +217,60 @@ class TCPHandle<T> {
|
|
|
217
217
|
const socket = connect({ host, port }).setKeepAlive(true)
|
|
218
218
|
this.buffer = Buffer.alloc(0)
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
// Mutable holder so upgradeToTls can swap the socket mid-handshake;
|
|
221
|
+
// the negotiator's sendSession reads .current at write time, so frames
|
|
222
|
+
// always go to the active socket even after the TLS upgrade.
|
|
223
|
+
const socketRef = { current: socket }
|
|
224
|
+
|
|
225
|
+
await new Promise<void>((resolve, reject) => {
|
|
226
|
+
let connected = false
|
|
227
|
+
|
|
221
228
|
socket.once('connect', () => {
|
|
229
|
+
connected = true
|
|
222
230
|
resolve()
|
|
223
231
|
})
|
|
224
232
|
|
|
225
233
|
socket.once('error', (err) => {
|
|
226
|
-
if (!
|
|
227
|
-
|
|
234
|
+
if (!connected) {
|
|
235
|
+
reject(err)
|
|
228
236
|
}
|
|
237
|
+
// Post-connect errors are followed by 'close', which drives reconnect;
|
|
238
|
+
// this listener exists only to prevent uncaughtException.
|
|
229
239
|
})
|
|
230
240
|
|
|
231
241
|
socket.once('close', () => {
|
|
232
|
-
socket
|
|
242
|
+
// socketRef.current is the TLS-upgraded socket if upgrade ran; the
|
|
243
|
+
// plain one otherwise. Both cases hold the only live data listener.
|
|
244
|
+
socketRef.current.removeAllListeners('data')
|
|
245
|
+
|
|
233
246
|
if (!this.closing) {
|
|
234
|
-
onClose()
|
|
247
|
+
if (connected) onClose()
|
|
235
248
|
|
|
236
249
|
this.connectionAttempts++
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
250
|
+
// Swallow the rejection of the now-orphaned previous promise; any
|
|
251
|
+
// in-flight get() awaiter still sees it via their captured reference.
|
|
252
|
+
const previous = this.currentSocketPromise
|
|
253
|
+
this.currentSocketPromise =
|
|
254
|
+
this.connectionAttempts < 3 ? this.connect(host, port, onConnected, onClose) : null
|
|
255
|
+
previous?.catch(() => undefined)
|
|
242
256
|
}
|
|
257
|
+
|
|
258
|
+
if (!connected) reject(new Error('Socket closed before connect'))
|
|
243
259
|
})
|
|
244
260
|
|
|
245
261
|
this.attachDataListener(socket)
|
|
246
262
|
})
|
|
247
263
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
264
|
+
try {
|
|
265
|
+
await onConnected(socketRef)
|
|
266
|
+
} catch (err) {
|
|
267
|
+
// Handshake failures (timeout, bad response) leave the socket open
|
|
268
|
+
// otherwise; destroy it so the 'close' handler drives reconnect.
|
|
269
|
+
socketRef.current.destroy()
|
|
270
|
+
throw err
|
|
271
|
+
}
|
|
254
272
|
this.connectionAttempts = 0
|
|
255
273
|
|
|
256
|
-
return
|
|
274
|
+
return socketRef.current
|
|
257
275
|
}
|
|
258
276
|
}
|
|
@@ -39,7 +39,7 @@ export class WebSocketSender extends OpenConnectionSender {
|
|
|
39
39
|
authentication: options.authentication,
|
|
40
40
|
})
|
|
41
41
|
},
|
|
42
|
-
() => this.
|
|
42
|
+
() => this.rejectPending('Connection was closed'),
|
|
43
43
|
(envelope: Envelope) => {
|
|
44
44
|
if (this.sessionNegotiator?.negotiating) {
|
|
45
45
|
return this.sessionNegotiator.handleEnvelope(envelope)
|
|
@@ -139,7 +139,10 @@ class WebSocketHandle<T> {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
public get() {
|
|
142
|
-
|
|
142
|
+
if (!this.currentWebSocketPromise) {
|
|
143
|
+
throw new Error('WebSocket connection is not available.')
|
|
144
|
+
}
|
|
145
|
+
return this.currentWebSocketPromise
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
public async close() {
|
|
@@ -159,31 +162,54 @@ class WebSocketHandle<T> {
|
|
|
159
162
|
const connection = new WebSocket(url, 'lime')
|
|
160
163
|
|
|
161
164
|
await new Promise<void>((resolve, reject) => {
|
|
165
|
+
let connected = false
|
|
166
|
+
|
|
162
167
|
connection.onopen = () => {
|
|
168
|
+
connected = true
|
|
163
169
|
resolve()
|
|
164
170
|
}
|
|
171
|
+
connection.onerror = (err) => {
|
|
172
|
+
if (!connected) {
|
|
173
|
+
reject('message' in err ? new Error(`WebSocket error: ${err.message}`, { cause: err }) : err)
|
|
174
|
+
}
|
|
175
|
+
// Post-open errors are followed by 'close', which drives reconnect.
|
|
176
|
+
}
|
|
165
177
|
connection.onclose = () => {
|
|
166
178
|
if (!this.closing) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (this.connectionAttempts < 3) {
|
|
170
|
-
this.currentWebSocketPromise = this.connect(url, onConnected, onClose, onMessage)
|
|
171
|
-
} else {
|
|
172
|
-
reject(new Error('Failed to connect to WebSocket'))
|
|
173
|
-
}
|
|
179
|
+
if (connected) onClose()
|
|
174
180
|
|
|
175
|
-
|
|
181
|
+
this.connectionAttempts++
|
|
182
|
+
// Swallow the rejection of the now-orphaned previous promise; any
|
|
183
|
+
// in-flight get() awaiter still sees it via their captured reference.
|
|
184
|
+
const previous = this.currentWebSocketPromise
|
|
185
|
+
this.currentWebSocketPromise =
|
|
186
|
+
this.connectionAttempts < 3 ? this.connect(url, onConnected, onClose, onMessage) : null
|
|
187
|
+
previous?.catch(() => undefined)
|
|
176
188
|
}
|
|
189
|
+
|
|
190
|
+
if (!connected) reject(new Error('WebSocket closed before open'))
|
|
177
191
|
}
|
|
178
192
|
connection.onmessage = (event) => {
|
|
179
193
|
onMessage(JSON.parse(event.data))
|
|
180
194
|
}
|
|
181
|
-
connection.onerror = (err) => {
|
|
182
|
-
reject('message' in err ? new Error(`WebSocket error: ${err.message}`, { cause: err }) : err)
|
|
183
|
-
}
|
|
184
195
|
})
|
|
185
196
|
|
|
186
|
-
|
|
197
|
+
try {
|
|
198
|
+
await onConnected(connection)
|
|
199
|
+
} catch (err) {
|
|
200
|
+
// Handshake failures leave the socket open otherwise; tear it down so
|
|
201
|
+
// the 'onclose' handler drives reconnect. Prefer ws.terminate() (hard
|
|
202
|
+
// kill, Node only) to avoid waiting on the graceful close handshake
|
|
203
|
+
// with a peer that just failed to negotiate; fall back to close() in
|
|
204
|
+
// browsers where terminate() doesn't exist.
|
|
205
|
+
const terminable = connection as WebSocket & { terminate?: () => void }
|
|
206
|
+
if (terminable.terminate) {
|
|
207
|
+
terminable.terminate()
|
|
208
|
+
} else {
|
|
209
|
+
connection.close()
|
|
210
|
+
}
|
|
211
|
+
throw err
|
|
212
|
+
}
|
|
187
213
|
this.connectionAttempts = 0
|
|
188
214
|
|
|
189
215
|
return connection
|
package/src/types/desk.ts
CHANGED
|
@@ -101,6 +101,25 @@ export type DeskRule = {
|
|
|
101
101
|
queueId?: string
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Filters accepted by every `/analytics/reports/*` endpoint. The backend
|
|
106
|
+
* applies all filters with AND semantics; arrays inside a single filter are
|
|
107
|
+
* OR'd together.
|
|
108
|
+
*/
|
|
109
|
+
export type ReportFilters = {
|
|
110
|
+
beginDate: string | Date
|
|
111
|
+
endDate: string | Date
|
|
112
|
+
teams?: Array<string>
|
|
113
|
+
tags?: Array<string>
|
|
114
|
+
agentIdentities?: Array<Identity>
|
|
115
|
+
customerIdentities?: Array<Identity>
|
|
116
|
+
domains?: Array<string>
|
|
117
|
+
/** Include SLA applied/achieved counts on agent rows. */
|
|
118
|
+
calculateSla?: boolean
|
|
119
|
+
/** Force a fresh aggregation on `/tags`, bypassing the server cache. */
|
|
120
|
+
refreshCache?: boolean
|
|
121
|
+
}
|
|
122
|
+
|
|
104
123
|
export type DeskPriorityRule = {
|
|
105
124
|
id?: string
|
|
106
125
|
ownerIdentity?: Identity
|