farvex 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +150 -224
- package/dist/index.cjs +652 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +652 -61
- package/dist/index.js.map +1 -1
- package/dist/livekit/index.cjs +43 -1
- package/dist/livekit/index.cjs.map +1 -1
- package/dist/livekit/index.d.cts +18 -0
- package/dist/livekit/index.d.ts +18 -0
- package/dist/livekit/index.js +46 -0
- package/dist/livekit/index.js.map +1 -1
- package/dist/react/index.cjs +17 -2
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +11 -8
- package/dist/react/index.d.ts +11 -8
- package/dist/react/index.js +16 -3
- package/dist/react/index.js.map +1 -1
- package/dist/{types-DhJEeeui.d.cts → types-BjVaSMZ6.d.cts} +68 -5
- package/dist/{types-DhJEeeui.d.ts → types-BjVaSMZ6.d.ts} +68 -5
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var libphonenumberJs = require('libphonenumber-js');
|
|
3
4
|
var centrifuge = require('centrifuge');
|
|
4
5
|
|
|
5
6
|
// src/api/generated/core/bodySerializer.gen.ts
|
|
@@ -804,7 +805,7 @@ var apiClient = (config) => {
|
|
|
804
805
|
var unwrap = async (result, auth) => {
|
|
805
806
|
const response = await result;
|
|
806
807
|
if (response.error !== void 0) {
|
|
807
|
-
if (response.response?.status === 401) {
|
|
808
|
+
if (response.response?.status === 401 || response.response?.status === 403) {
|
|
808
809
|
await auth?.onUnauthorized?.();
|
|
809
810
|
}
|
|
810
811
|
throw errorFrom(response.error, response.response?.status);
|
|
@@ -943,9 +944,23 @@ var createEmitter = () => {
|
|
|
943
944
|
emit
|
|
944
945
|
};
|
|
945
946
|
};
|
|
946
|
-
var
|
|
947
|
+
var normalizeE164Phone = (phone, defaultCountry = "NG") => {
|
|
948
|
+
const trimmed = phone.trim();
|
|
949
|
+
const parsed = libphonenumberJs.parsePhoneNumberFromString(trimmed);
|
|
950
|
+
if (parsed?.isValid() && parsed.country) {
|
|
951
|
+
return parsed.number;
|
|
952
|
+
}
|
|
953
|
+
const fallback = libphonenumberJs.parsePhoneNumberFromString(trimmed, defaultCountry);
|
|
954
|
+
if (fallback?.isValid()) {
|
|
955
|
+
return fallback.number;
|
|
956
|
+
}
|
|
957
|
+
throw new Error("Enter a valid phone number");
|
|
958
|
+
};
|
|
959
|
+
var createRealtime = (client2, auth, store, emitter, recover) => {
|
|
947
960
|
let rt = null;
|
|
961
|
+
let connectPromise = null;
|
|
948
962
|
let disposed = false;
|
|
963
|
+
let attempt = 0;
|
|
949
964
|
const fetchToken = async () => unwrap(getRealtimeToken({ client: client2 }), auth);
|
|
950
965
|
const refreshToken = async () => {
|
|
951
966
|
try {
|
|
@@ -982,6 +997,8 @@ var createRealtime = (client2, auth, store, emitter) => {
|
|
|
982
997
|
emitter.emit("status", status);
|
|
983
998
|
};
|
|
984
999
|
const disconnect = async () => {
|
|
1000
|
+
attempt += 1;
|
|
1001
|
+
connectPromise = null;
|
|
985
1002
|
rt?.disconnect();
|
|
986
1003
|
rt = null;
|
|
987
1004
|
if (!disposed) {
|
|
@@ -990,33 +1007,50 @@ var createRealtime = (client2, auth, store, emitter) => {
|
|
|
990
1007
|
};
|
|
991
1008
|
return {
|
|
992
1009
|
connect: async () => {
|
|
1010
|
+
if (connectPromise) {
|
|
1011
|
+
return connectPromise;
|
|
1012
|
+
}
|
|
993
1013
|
if (rt) {
|
|
994
1014
|
return;
|
|
995
1015
|
}
|
|
996
1016
|
disposed = false;
|
|
997
|
-
|
|
998
|
-
|
|
1017
|
+
const currentAttempt = ++attempt;
|
|
1018
|
+
const promise = (async () => {
|
|
1019
|
+
setStatus("connecting");
|
|
999
1020
|
const first = await fetchToken();
|
|
1000
|
-
|
|
1021
|
+
if (disposed || currentAttempt !== attempt) {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
const next = new centrifuge.Centrifuge(first.url, {
|
|
1001
1025
|
token: first.token,
|
|
1002
1026
|
getToken: refreshToken
|
|
1003
1027
|
});
|
|
1004
|
-
rt
|
|
1005
|
-
|
|
1006
|
-
|
|
1028
|
+
rt = next;
|
|
1029
|
+
next.on("connecting", () => {
|
|
1030
|
+
const status = store.getStatus() === "ready" ? "reconnecting" : "connecting";
|
|
1031
|
+
setStatus(status);
|
|
1007
1032
|
});
|
|
1008
|
-
|
|
1033
|
+
next.on("connected", () => {
|
|
1034
|
+
const shouldRecover = store.getStatus() === "reconnecting" || store.getStatus() === "offline";
|
|
1009
1035
|
setStatus("ready");
|
|
1036
|
+
if (shouldRecover && recover) {
|
|
1037
|
+
void recover().catch((err) => {
|
|
1038
|
+
emitter.emit(
|
|
1039
|
+
"error",
|
|
1040
|
+
err instanceof Error ? err : new Error("Realtime recovery failed")
|
|
1041
|
+
);
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1010
1044
|
});
|
|
1011
|
-
|
|
1045
|
+
next.on("disconnected", () => {
|
|
1012
1046
|
if (!disposed) {
|
|
1013
1047
|
setStatus("offline");
|
|
1014
1048
|
}
|
|
1015
1049
|
});
|
|
1016
|
-
|
|
1050
|
+
next.on("error", (ctx) => {
|
|
1017
1051
|
emitter.emit("error", new Error(ctx.error.message));
|
|
1018
1052
|
});
|
|
1019
|
-
|
|
1053
|
+
next.on("publication", (ctx) => {
|
|
1020
1054
|
try {
|
|
1021
1055
|
const message = parseMessage(ctx.data);
|
|
1022
1056
|
if (message) {
|
|
@@ -1026,16 +1060,34 @@ var createRealtime = (client2, auth, store, emitter) => {
|
|
|
1026
1060
|
emitter.emit("error", err instanceof Error ? err : new Error("Realtime event failed"));
|
|
1027
1061
|
}
|
|
1028
1062
|
});
|
|
1029
|
-
|
|
1030
|
-
await
|
|
1063
|
+
next.connect();
|
|
1064
|
+
await next.ready();
|
|
1065
|
+
if (disposed || currentAttempt !== attempt) {
|
|
1066
|
+
next.disconnect();
|
|
1067
|
+
if (rt === next) {
|
|
1068
|
+
rt = null;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
})();
|
|
1072
|
+
connectPromise = promise;
|
|
1073
|
+
try {
|
|
1074
|
+
await promise;
|
|
1031
1075
|
} catch (err) {
|
|
1032
|
-
|
|
1076
|
+
if (currentAttempt === attempt) {
|
|
1077
|
+
await disconnect();
|
|
1078
|
+
}
|
|
1033
1079
|
throw err;
|
|
1080
|
+
} finally {
|
|
1081
|
+
if (connectPromise === promise) {
|
|
1082
|
+
connectPromise = null;
|
|
1083
|
+
}
|
|
1034
1084
|
}
|
|
1035
1085
|
},
|
|
1036
1086
|
disconnect,
|
|
1037
1087
|
dispose: async () => {
|
|
1038
1088
|
disposed = true;
|
|
1089
|
+
attempt += 1;
|
|
1090
|
+
connectPromise = null;
|
|
1039
1091
|
rt?.disconnect();
|
|
1040
1092
|
rt = null;
|
|
1041
1093
|
setStatus("disposed");
|
|
@@ -1047,24 +1099,453 @@ var parseMessage = (value) => {
|
|
|
1047
1099
|
return null;
|
|
1048
1100
|
}
|
|
1049
1101
|
const message = value;
|
|
1050
|
-
if (message.event === "session.upsert" && message.data) {
|
|
1051
|
-
return message;
|
|
1102
|
+
if (message.event === "session.upsert" && isVoiceSession(message.data)) {
|
|
1103
|
+
return { event: "session.upsert", data: message.data };
|
|
1052
1104
|
}
|
|
1053
|
-
if (message.event === "session.remove" && message.data) {
|
|
1054
|
-
return message;
|
|
1105
|
+
if (message.event === "session.remove" && isRemovePayload(message.data)) {
|
|
1106
|
+
return { event: "session.remove", data: message.data };
|
|
1055
1107
|
}
|
|
1056
1108
|
return null;
|
|
1057
1109
|
};
|
|
1110
|
+
var isVoiceSession = (value) => {
|
|
1111
|
+
if (!value || typeof value !== "object") {
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
const session = value;
|
|
1115
|
+
return typeof session.id === "string" && typeof session.vendor === "string" && typeof session.version === "number" && (session.state === "ringing" || session.state === "active" || session.state === "ended") && Array.isArray(session.participants);
|
|
1116
|
+
};
|
|
1117
|
+
var isRemovePayload = (value) => {
|
|
1118
|
+
if (!value || typeof value !== "object") {
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
const payload = value;
|
|
1122
|
+
return typeof payload.id === "string" && (payload.vendor === void 0 || typeof payload.vendor === "string") && (payload.version === void 0 || typeof payload.version === "number");
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
// src/core/session.ts
|
|
1126
|
+
var createCurrentSessionController = (deps) => {
|
|
1127
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
1128
|
+
const cleanup = [];
|
|
1129
|
+
let current = null;
|
|
1130
|
+
let pending = null;
|
|
1131
|
+
let op = 0;
|
|
1132
|
+
let mediaConnected = false;
|
|
1133
|
+
let snapshot = {
|
|
1134
|
+
current,
|
|
1135
|
+
incoming: deps.store.invites(),
|
|
1136
|
+
busy: false
|
|
1137
|
+
};
|
|
1138
|
+
const publish = () => {
|
|
1139
|
+
const next = {
|
|
1140
|
+
current,
|
|
1141
|
+
incoming: deps.store.invites(),
|
|
1142
|
+
busy: pending !== null || isBusy(current)
|
|
1143
|
+
};
|
|
1144
|
+
if (snapshot.current === next.current && snapshot.incoming === next.incoming && snapshot.busy === next.busy) {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
snapshot = next;
|
|
1148
|
+
for (const fn of subscribers) {
|
|
1149
|
+
fn();
|
|
1150
|
+
}
|
|
1151
|
+
deps.emitter.emit("session.current.changed", snapshot);
|
|
1152
|
+
};
|
|
1153
|
+
const commit = (next) => {
|
|
1154
|
+
const prev = current;
|
|
1155
|
+
current = next;
|
|
1156
|
+
publish();
|
|
1157
|
+
if (!next) {
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
if (!prev || prev.sessionId !== next.sessionId) {
|
|
1161
|
+
deps.emitter.emit("session.current.started", next);
|
|
1162
|
+
}
|
|
1163
|
+
if (prev?.phase !== "active" && next.phase === "active") {
|
|
1164
|
+
deps.emitter.emit("session.current.connected", next);
|
|
1165
|
+
}
|
|
1166
|
+
if (prev?.phase !== "ended" && next.phase === "ended") {
|
|
1167
|
+
deps.emitter.emit("session.current.ended", next);
|
|
1168
|
+
}
|
|
1169
|
+
if (prev?.phase !== "failed" && next.phase === "failed" && next.error) {
|
|
1170
|
+
deps.emitter.emit("session.current.failed", { session: next, error: next.error });
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
const commitFailed = (session, err) => {
|
|
1174
|
+
const error = toCallpadError(err);
|
|
1175
|
+
if (session) {
|
|
1176
|
+
commit(
|
|
1177
|
+
fromSession(session, {
|
|
1178
|
+
phase: "failed",
|
|
1179
|
+
join: null,
|
|
1180
|
+
participantId: null,
|
|
1181
|
+
startedBy: current?.startedBy ?? "join",
|
|
1182
|
+
error
|
|
1183
|
+
})
|
|
1184
|
+
);
|
|
1185
|
+
} else {
|
|
1186
|
+
deps.emitter.emit("session.current.failed", { session: current, error });
|
|
1187
|
+
publish();
|
|
1188
|
+
}
|
|
1189
|
+
return error;
|
|
1190
|
+
};
|
|
1191
|
+
const nextOp = () => {
|
|
1192
|
+
op += 1;
|
|
1193
|
+
return op;
|
|
1194
|
+
};
|
|
1195
|
+
const run = (kind, task) => {
|
|
1196
|
+
if (pending) {
|
|
1197
|
+
if (kind === "end" && pending.kind === "end") {
|
|
1198
|
+
return pending.promise;
|
|
1199
|
+
}
|
|
1200
|
+
return Promise.reject(busyError());
|
|
1201
|
+
}
|
|
1202
|
+
const id = nextOp();
|
|
1203
|
+
const promise = task(id).finally(() => {
|
|
1204
|
+
if (pending?.op === id) {
|
|
1205
|
+
pending = null;
|
|
1206
|
+
publish();
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
pending = { kind, op: id, promise };
|
|
1210
|
+
publish();
|
|
1211
|
+
return promise;
|
|
1212
|
+
};
|
|
1213
|
+
const assertFresh = (id) => {
|
|
1214
|
+
if (id !== op) {
|
|
1215
|
+
throw new CallpadError("session.command_stale", "Session command was superseded");
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
const canReplaceCurrent = (replaceCurrent) => {
|
|
1219
|
+
return replaceCurrent === true || !isBusy(current);
|
|
1220
|
+
};
|
|
1221
|
+
const reconcile = () => {
|
|
1222
|
+
if (!current) {
|
|
1223
|
+
publish();
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const session = deps.store.get(current.sessionId);
|
|
1227
|
+
if (!session) {
|
|
1228
|
+
mediaConnected = false;
|
|
1229
|
+
commit({
|
|
1230
|
+
...current,
|
|
1231
|
+
phase: "ended",
|
|
1232
|
+
session: null,
|
|
1233
|
+
join: null,
|
|
1234
|
+
endedAt: current.endedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1235
|
+
});
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
const next = reconcileCurrent(current, session, mediaConnected);
|
|
1239
|
+
if (next !== current) {
|
|
1240
|
+
if (isTerminal(next.phase)) {
|
|
1241
|
+
mediaConnected = false;
|
|
1242
|
+
}
|
|
1243
|
+
commit(next);
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
publish();
|
|
1247
|
+
};
|
|
1248
|
+
cleanup.push(deps.store.subscribe("sessions", reconcile));
|
|
1249
|
+
cleanup.push(deps.store.subscribe("invites", publish));
|
|
1250
|
+
const start = (input, options = {}) => {
|
|
1251
|
+
const behavior = options.behavior ?? "rejectWhileBusy";
|
|
1252
|
+
if (behavior !== "allowParallel" && isBusy(current)) {
|
|
1253
|
+
if (behavior === "focusExisting") {
|
|
1254
|
+
const existing = findTargetSession(deps.store.list(), input.target);
|
|
1255
|
+
if (existing) {
|
|
1256
|
+
return join(existing.id, { replaceCurrent: true });
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return Promise.reject(busyError());
|
|
1260
|
+
}
|
|
1261
|
+
return run("start", async (id) => {
|
|
1262
|
+
try {
|
|
1263
|
+
const started = await deps.start(input);
|
|
1264
|
+
assertFresh(id);
|
|
1265
|
+
const next = fromStarted(started, "start", options.media);
|
|
1266
|
+
mediaConnected = false;
|
|
1267
|
+
commit(next);
|
|
1268
|
+
if (next.phase === "failed" && next.error) {
|
|
1269
|
+
throw next.error;
|
|
1270
|
+
}
|
|
1271
|
+
return next;
|
|
1272
|
+
} catch (err) {
|
|
1273
|
+
if (shouldRethrowCommandError(err)) {
|
|
1274
|
+
throw err;
|
|
1275
|
+
}
|
|
1276
|
+
throw commitFailed(null, err);
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
};
|
|
1280
|
+
const accept = (input, options = {}) => {
|
|
1281
|
+
if (!canReplaceCurrent(options.replaceCurrent)) {
|
|
1282
|
+
return Promise.reject(busyError());
|
|
1283
|
+
}
|
|
1284
|
+
return run("accept", async (id) => {
|
|
1285
|
+
try {
|
|
1286
|
+
const started = await deps.accept(input);
|
|
1287
|
+
assertFresh(id);
|
|
1288
|
+
const next = fromStarted(started, "accept", options.media);
|
|
1289
|
+
mediaConnected = false;
|
|
1290
|
+
commit(next);
|
|
1291
|
+
if (next.phase === "failed" && next.error) {
|
|
1292
|
+
throw next.error;
|
|
1293
|
+
}
|
|
1294
|
+
return next;
|
|
1295
|
+
} catch (err) {
|
|
1296
|
+
if (shouldRethrowCommandError(err)) {
|
|
1297
|
+
throw err;
|
|
1298
|
+
}
|
|
1299
|
+
throw commitFailed(deps.store.get(input.sessionId), err);
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1302
|
+
};
|
|
1303
|
+
const join = (sessionId, options = {}) => {
|
|
1304
|
+
if (!canReplaceCurrent(options.replaceCurrent)) {
|
|
1305
|
+
return Promise.reject(busyError());
|
|
1306
|
+
}
|
|
1307
|
+
const session = deps.store.get(sessionId);
|
|
1308
|
+
if (!session) {
|
|
1309
|
+
return Promise.reject(new CallpadError("session.not_found", "Session was not found"));
|
|
1310
|
+
}
|
|
1311
|
+
return run("join", async (id) => {
|
|
1312
|
+
try {
|
|
1313
|
+
const grant = await deps.join(sessionId);
|
|
1314
|
+
assertFresh(id);
|
|
1315
|
+
const latest = deps.store.get(sessionId) ?? session;
|
|
1316
|
+
const next = fromSession(latest, {
|
|
1317
|
+
phase: latest.state === "active" ? "active" : "connecting",
|
|
1318
|
+
join: grant,
|
|
1319
|
+
participantId: grant.participantId,
|
|
1320
|
+
startedBy: "join",
|
|
1321
|
+
error: null
|
|
1322
|
+
});
|
|
1323
|
+
mediaConnected = next.phase === "active";
|
|
1324
|
+
commit(next);
|
|
1325
|
+
return next;
|
|
1326
|
+
} catch (err) {
|
|
1327
|
+
if (shouldRethrowCommandError(err)) {
|
|
1328
|
+
throw err;
|
|
1329
|
+
}
|
|
1330
|
+
throw commitFailed(session, err);
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
};
|
|
1334
|
+
const end = async () => {
|
|
1335
|
+
const target = current;
|
|
1336
|
+
if (!target || isTerminal(target.phase)) {
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
await run("end", async (id) => {
|
|
1340
|
+
const session = deps.store.get(target.sessionId);
|
|
1341
|
+
commit({ ...target, phase: "ending" });
|
|
1342
|
+
try {
|
|
1343
|
+
if (session && deps.end && deps.store.can(session, "end")) {
|
|
1344
|
+
await deps.end(target.sessionId);
|
|
1345
|
+
} else {
|
|
1346
|
+
await deps.leave(target.sessionId);
|
|
1347
|
+
}
|
|
1348
|
+
assertFresh(id);
|
|
1349
|
+
mediaConnected = false;
|
|
1350
|
+
const ended = deps.store.get(target.sessionId);
|
|
1351
|
+
commit({
|
|
1352
|
+
...target,
|
|
1353
|
+
phase: "ended",
|
|
1354
|
+
session: ended,
|
|
1355
|
+
join: null,
|
|
1356
|
+
endedAt: ended?.endedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1357
|
+
});
|
|
1358
|
+
} catch (err) {
|
|
1359
|
+
if (shouldRethrowCommandError(err)) {
|
|
1360
|
+
throw err;
|
|
1361
|
+
}
|
|
1362
|
+
throw commitFailed(session, err);
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
};
|
|
1366
|
+
const clear = () => {
|
|
1367
|
+
nextOp();
|
|
1368
|
+
pending = null;
|
|
1369
|
+
mediaConnected = false;
|
|
1370
|
+
commit(null);
|
|
1371
|
+
};
|
|
1372
|
+
const mediaConnectedFor = (sessionId) => {
|
|
1373
|
+
if (!current || sessionId && current.sessionId !== sessionId || isTerminal(current.phase)) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
mediaConnected = true;
|
|
1377
|
+
const session = deps.store.get(current.sessionId);
|
|
1378
|
+
commit({
|
|
1379
|
+
...current,
|
|
1380
|
+
phase: "active",
|
|
1381
|
+
session,
|
|
1382
|
+
answeredAt: session?.answeredAt ?? current.answeredAt
|
|
1383
|
+
});
|
|
1384
|
+
};
|
|
1385
|
+
const mediaDisconnectedFor = (sessionId) => {
|
|
1386
|
+
if (!current || sessionId && current.sessionId !== sessionId || isTerminal(current.phase)) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
mediaConnected = false;
|
|
1390
|
+
const session = deps.store.get(current.sessionId);
|
|
1391
|
+
if (session?.state === "ended") {
|
|
1392
|
+
reconcile();
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
commit({
|
|
1396
|
+
...current,
|
|
1397
|
+
phase: current.join ? "connecting" : current.direction === "inbound" ? "incoming" : "outgoing",
|
|
1398
|
+
session
|
|
1399
|
+
});
|
|
1400
|
+
};
|
|
1401
|
+
const mediaFailedFor = (error, sessionId) => {
|
|
1402
|
+
if (!current || sessionId && current.sessionId !== sessionId || isTerminal(current.phase)) {
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
mediaConnected = false;
|
|
1406
|
+
commit({
|
|
1407
|
+
...current,
|
|
1408
|
+
phase: "failed",
|
|
1409
|
+
join: null,
|
|
1410
|
+
error: toCallpadError(error, "session.media_failed")
|
|
1411
|
+
});
|
|
1412
|
+
};
|
|
1413
|
+
return {
|
|
1414
|
+
get: () => snapshot,
|
|
1415
|
+
start,
|
|
1416
|
+
accept,
|
|
1417
|
+
join,
|
|
1418
|
+
end,
|
|
1419
|
+
clear,
|
|
1420
|
+
mediaConnected: mediaConnectedFor,
|
|
1421
|
+
mediaDisconnected: mediaDisconnectedFor,
|
|
1422
|
+
mediaFailed: mediaFailedFor,
|
|
1423
|
+
subscribe: (fn) => {
|
|
1424
|
+
subscribers.add(fn);
|
|
1425
|
+
return () => {
|
|
1426
|
+
subscribers.delete(fn);
|
|
1427
|
+
};
|
|
1428
|
+
},
|
|
1429
|
+
dispose: () => {
|
|
1430
|
+
for (const unsubscribe of cleanup) {
|
|
1431
|
+
unsubscribe();
|
|
1432
|
+
}
|
|
1433
|
+
subscribers.clear();
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
};
|
|
1437
|
+
var fromStarted = (started, startedBy, media = "required") => {
|
|
1438
|
+
if (!started.join && media === "required") {
|
|
1439
|
+
return fromSession(started.session, {
|
|
1440
|
+
phase: "failed",
|
|
1441
|
+
join: null,
|
|
1442
|
+
participantId: null,
|
|
1443
|
+
startedBy,
|
|
1444
|
+
error: new CallpadError(
|
|
1445
|
+
"session.join_unavailable",
|
|
1446
|
+
"Session did not include a browser media join grant"
|
|
1447
|
+
)
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
const phase = started.join ? started.session.state === "active" ? "active" : "connecting" : started.session.direction === "inbound" ? "incoming" : "outgoing";
|
|
1451
|
+
return fromSession(started.session, {
|
|
1452
|
+
phase,
|
|
1453
|
+
join: started.join,
|
|
1454
|
+
participantId: started.join?.participantId ?? null,
|
|
1455
|
+
startedBy,
|
|
1456
|
+
error: null
|
|
1457
|
+
});
|
|
1458
|
+
};
|
|
1459
|
+
var fromSession = (session, values) => ({
|
|
1460
|
+
phase: values.phase,
|
|
1461
|
+
direction: session.direction,
|
|
1462
|
+
sessionId: session.id,
|
|
1463
|
+
participantId: values.participantId,
|
|
1464
|
+
session,
|
|
1465
|
+
join: values.join,
|
|
1466
|
+
startedBy: values.startedBy,
|
|
1467
|
+
error: values.error,
|
|
1468
|
+
createdAt: session.createdAt,
|
|
1469
|
+
answeredAt: session.answeredAt,
|
|
1470
|
+
endedAt: session.endedAt
|
|
1471
|
+
});
|
|
1472
|
+
var reconcileCurrent = (current, session, mediaConnected) => {
|
|
1473
|
+
if (session.state === "ended") {
|
|
1474
|
+
return {
|
|
1475
|
+
...current,
|
|
1476
|
+
phase: "ended",
|
|
1477
|
+
session,
|
|
1478
|
+
join: null,
|
|
1479
|
+
answeredAt: session.answeredAt,
|
|
1480
|
+
endedAt: session.endedAt ?? current.endedAt
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
if (isTerminal(current.phase)) {
|
|
1484
|
+
return {
|
|
1485
|
+
...current,
|
|
1486
|
+
session,
|
|
1487
|
+
answeredAt: session.answeredAt,
|
|
1488
|
+
endedAt: session.endedAt
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
const phase = mediaConnected || session.state === "active" ? "active" : current.join ? "connecting" : current.phase === "ending" ? "ending" : session.direction === "inbound" ? "incoming" : "outgoing";
|
|
1492
|
+
if (current.phase === phase && current.session === session && current.answeredAt === session.answeredAt && current.endedAt === session.endedAt) {
|
|
1493
|
+
return current;
|
|
1494
|
+
}
|
|
1495
|
+
return {
|
|
1496
|
+
...current,
|
|
1497
|
+
phase,
|
|
1498
|
+
session,
|
|
1499
|
+
answeredAt: session.answeredAt,
|
|
1500
|
+
endedAt: session.endedAt
|
|
1501
|
+
};
|
|
1502
|
+
};
|
|
1503
|
+
var findTargetSession = (sessions, target) => {
|
|
1504
|
+
const normalized = normalizeTarget(target);
|
|
1505
|
+
return sessions.find(
|
|
1506
|
+
(session) => session.state !== "ended" && session.participants.some((participant) => {
|
|
1507
|
+
if (normalized.type === "vendor_user") {
|
|
1508
|
+
return participant.kind === "vendor_user" && participant.userId === normalized.userId;
|
|
1509
|
+
}
|
|
1510
|
+
if (normalized.type === "customer") {
|
|
1511
|
+
return participant.kind === "customer" && participant.userId === normalized.userId;
|
|
1512
|
+
}
|
|
1513
|
+
if (normalized.type === "contact") {
|
|
1514
|
+
return participant.contactId === normalized.contactId;
|
|
1515
|
+
}
|
|
1516
|
+
return participant.phone === normalized.phone;
|
|
1517
|
+
})
|
|
1518
|
+
) ?? null;
|
|
1519
|
+
};
|
|
1520
|
+
var normalizeTarget = (target) => {
|
|
1521
|
+
if (target.type === "phone") {
|
|
1522
|
+
return { ...target, phone: normalizeE164Phone(target.phone) };
|
|
1523
|
+
}
|
|
1524
|
+
return target;
|
|
1525
|
+
};
|
|
1526
|
+
var isBusy = (session) => session !== null && !isTerminal(session.phase);
|
|
1527
|
+
var isTerminal = (phase) => phase === "ended" || phase === "failed";
|
|
1528
|
+
var busyError = () => new CallpadError("session.busy", "A session is already active");
|
|
1529
|
+
var shouldRethrowCommandError = (err) => err instanceof CallpadError && (err.code === "session.join_unavailable" || err.code === "session.command_stale");
|
|
1530
|
+
var toCallpadError = (err, code = "session.failed") => {
|
|
1531
|
+
if (err instanceof CallpadError) {
|
|
1532
|
+
return err;
|
|
1533
|
+
}
|
|
1534
|
+
if (err instanceof Error) {
|
|
1535
|
+
return new CallpadError(code, err.message);
|
|
1536
|
+
}
|
|
1537
|
+
return new CallpadError(code, "Session command failed");
|
|
1538
|
+
};
|
|
1058
1539
|
|
|
1059
1540
|
// src/core/store.ts
|
|
1060
1541
|
var createSessionStore = (userId) => {
|
|
1061
1542
|
const sessions = /* @__PURE__ */ new Map();
|
|
1062
1543
|
const subscribers = /* @__PURE__ */ new Map();
|
|
1063
1544
|
let status = "idle";
|
|
1064
|
-
let activeId = null;
|
|
1065
1545
|
let listCache = [];
|
|
1066
1546
|
let invitesCache = [];
|
|
1067
1547
|
let inviteKey = "";
|
|
1548
|
+
let inviteExpiryTimer = null;
|
|
1068
1549
|
const emit = (topic) => {
|
|
1069
1550
|
const fns = subscribers.get(topic);
|
|
1070
1551
|
if (!fns) {
|
|
@@ -1092,18 +1573,11 @@ var createSessionStore = (userId) => {
|
|
|
1092
1573
|
listCache = Array.from(sessions.values()).sort(
|
|
1093
1574
|
(a, b) => b.createdAt.localeCompare(a.createdAt)
|
|
1094
1575
|
);
|
|
1095
|
-
if (activeId) {
|
|
1096
|
-
const active = sessions.get(activeId);
|
|
1097
|
-
if (!active || active.state === "ended") {
|
|
1098
|
-
activeId = null;
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
if (!activeId) {
|
|
1102
|
-
activeId = listCache.find((session) => session.state !== "ended")?.id ?? null;
|
|
1103
|
-
}
|
|
1104
1576
|
};
|
|
1105
1577
|
const refreshInvites = () => {
|
|
1106
|
-
const
|
|
1578
|
+
const now = Date.now();
|
|
1579
|
+
const invites = listCache.map((session) => inviteFrom(session, userId, now)).filter((invite) => invite !== null);
|
|
1580
|
+
scheduleInviteExpiry(invites, now);
|
|
1107
1581
|
const nextKey = invites.map(
|
|
1108
1582
|
(invite) => `${invite.session.id}:${invite.participant.id}:${invite.participant.state}:${invite.participant.inviteExpiresAt ?? ""}`
|
|
1109
1583
|
).join("|");
|
|
@@ -1115,6 +1589,35 @@ var createSessionStore = (userId) => {
|
|
|
1115
1589
|
invitesCache = invites;
|
|
1116
1590
|
return true;
|
|
1117
1591
|
};
|
|
1592
|
+
const clearInviteExpiryTimer = () => {
|
|
1593
|
+
if (inviteExpiryTimer) {
|
|
1594
|
+
globalThis.clearTimeout(inviteExpiryTimer);
|
|
1595
|
+
inviteExpiryTimer = null;
|
|
1596
|
+
}
|
|
1597
|
+
};
|
|
1598
|
+
const scheduleInviteExpiry = (invites, now) => {
|
|
1599
|
+
clearInviteExpiryTimer();
|
|
1600
|
+
const nextExpiry = invites.reduce((min, invite) => {
|
|
1601
|
+
const expiresAt = inviteExpiryMs(invite.participant);
|
|
1602
|
+
if (expiresAt === null) {
|
|
1603
|
+
return min;
|
|
1604
|
+
}
|
|
1605
|
+
return min === null ? expiresAt : Math.min(min, expiresAt);
|
|
1606
|
+
}, null);
|
|
1607
|
+
if (nextExpiry === null) {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
inviteExpiryTimer = globalThis.setTimeout(
|
|
1611
|
+
() => {
|
|
1612
|
+
inviteExpiryTimer = null;
|
|
1613
|
+
if (refreshInvites()) {
|
|
1614
|
+
emit("invites");
|
|
1615
|
+
emit("sessions");
|
|
1616
|
+
}
|
|
1617
|
+
},
|
|
1618
|
+
Math.max(0, nextExpiry - now)
|
|
1619
|
+
);
|
|
1620
|
+
};
|
|
1118
1621
|
const notifySessionChange = (sessionId) => {
|
|
1119
1622
|
refresh();
|
|
1120
1623
|
const invitesChanged = refreshInvites();
|
|
@@ -1124,15 +1627,7 @@ var createSessionStore = (userId) => {
|
|
|
1124
1627
|
emit("invites");
|
|
1125
1628
|
}
|
|
1126
1629
|
};
|
|
1127
|
-
const get = (sessionId) =>
|
|
1128
|
-
if (sessionId) {
|
|
1129
|
-
return sessions.get(sessionId) ?? null;
|
|
1130
|
-
}
|
|
1131
|
-
if (!activeId) {
|
|
1132
|
-
return null;
|
|
1133
|
-
}
|
|
1134
|
-
return sessions.get(activeId) ?? null;
|
|
1135
|
-
};
|
|
1630
|
+
const get = (sessionId) => sessions.get(sessionId) ?? null;
|
|
1136
1631
|
const upsert = (session, mode = "event") => {
|
|
1137
1632
|
const prev = sessions.get(session.id);
|
|
1138
1633
|
const stale = prev ? mode === "command" ? session.version < prev.version : session.version <= prev.version : false;
|
|
@@ -1156,18 +1651,33 @@ var createSessionStore = (userId) => {
|
|
|
1156
1651
|
get,
|
|
1157
1652
|
invites: () => invitesCache,
|
|
1158
1653
|
invite: (sessionId) => invitesCache.find((invite) => invite.session.id === sessionId) ?? null,
|
|
1159
|
-
sync: (items) => {
|
|
1654
|
+
sync: (items, options) => {
|
|
1655
|
+
const changedIds = /* @__PURE__ */ new Set();
|
|
1160
1656
|
for (const session of items) {
|
|
1161
1657
|
const prev = sessions.get(session.id);
|
|
1162
1658
|
if (!prev || session.version >= prev.version) {
|
|
1163
1659
|
sessions.set(session.id, session);
|
|
1660
|
+
changedIds.add(session.id);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
const removedIds = [];
|
|
1664
|
+
if (options?.mode === "replaceVisible" && options.shouldReplace) {
|
|
1665
|
+
const visibleIds = new Set(items.map((session) => session.id));
|
|
1666
|
+
for (const session of sessions.values()) {
|
|
1667
|
+
if (!visibleIds.has(session.id) && options.shouldReplace(session)) {
|
|
1668
|
+
sessions.delete(session.id);
|
|
1669
|
+
removedIds.push(session.id);
|
|
1670
|
+
}
|
|
1164
1671
|
}
|
|
1165
1672
|
}
|
|
1166
1673
|
refresh();
|
|
1167
1674
|
const invitesChanged = refreshInvites();
|
|
1168
1675
|
emit("sessions");
|
|
1169
|
-
for (const
|
|
1170
|
-
emit(`session:${
|
|
1676
|
+
for (const sessionId of changedIds) {
|
|
1677
|
+
emit(`session:${sessionId}`);
|
|
1678
|
+
}
|
|
1679
|
+
for (const sessionId of removedIds) {
|
|
1680
|
+
emit(`session:${sessionId}`);
|
|
1171
1681
|
}
|
|
1172
1682
|
if (invitesChanged) {
|
|
1173
1683
|
emit("invites");
|
|
@@ -1201,13 +1711,17 @@ var createSessionStore = (userId) => {
|
|
|
1201
1711
|
return next;
|
|
1202
1712
|
},
|
|
1203
1713
|
can: (session, action) => can(session, userId, action),
|
|
1714
|
+
dispose: () => {
|
|
1715
|
+
clearInviteExpiryTimer();
|
|
1716
|
+
subscribers.clear();
|
|
1717
|
+
},
|
|
1204
1718
|
subscribe
|
|
1205
1719
|
};
|
|
1206
1720
|
};
|
|
1207
1721
|
var selfParticipant = (session, userId) => session.participants.find((participant) => participant.userId === userId) ?? null;
|
|
1208
|
-
var inviteFrom = (session, userId) => {
|
|
1722
|
+
var inviteFrom = (session, userId, now) => {
|
|
1209
1723
|
const participant = selfParticipant(session, userId);
|
|
1210
|
-
if (!participant ||
|
|
1724
|
+
if (!participant || session.state === "ended" || !isActiveInvite(participant, now)) {
|
|
1211
1725
|
return null;
|
|
1212
1726
|
}
|
|
1213
1727
|
return { session, participant };
|
|
@@ -1220,7 +1734,7 @@ var can = (session, userId, action) => {
|
|
|
1220
1734
|
switch (action) {
|
|
1221
1735
|
case "accept":
|
|
1222
1736
|
case "reject":
|
|
1223
|
-
return self
|
|
1737
|
+
return isActiveInvite(self);
|
|
1224
1738
|
case "join":
|
|
1225
1739
|
return self.transport === "webrtc" && (self.state === "accepted" || self.state === "joined");
|
|
1226
1740
|
case "leave":
|
|
@@ -1235,60 +1749,113 @@ var can = (session, userId, action) => {
|
|
|
1235
1749
|
return isHost(self) && session.recording.status === "recording";
|
|
1236
1750
|
}
|
|
1237
1751
|
};
|
|
1752
|
+
var isActiveInvite = (participant, now = Date.now()) => {
|
|
1753
|
+
const expiresAt = inviteExpiryMs(participant);
|
|
1754
|
+
return participant.state === "invited" && expiresAt !== null && expiresAt > now;
|
|
1755
|
+
};
|
|
1756
|
+
var inviteExpiryMs = (participant) => {
|
|
1757
|
+
if (!participant.inviteExpiresAt) {
|
|
1758
|
+
return null;
|
|
1759
|
+
}
|
|
1760
|
+
const expiresAt = Date.parse(participant.inviteExpiresAt);
|
|
1761
|
+
return Number.isFinite(expiresAt) ? expiresAt : null;
|
|
1762
|
+
};
|
|
1238
1763
|
var isHost = (participant) => participant.role === "host" && (participant.kind === "agent" || participant.kind === "vendor_user");
|
|
1239
1764
|
|
|
1240
1765
|
// src/core/client.ts
|
|
1241
1766
|
var createSessionClient = (config) => {
|
|
1242
1767
|
const core = createCore(config);
|
|
1768
|
+
const sessions = {
|
|
1769
|
+
...createCommonSessions(core),
|
|
1770
|
+
start: (input) => startSession(core, input.vendor, input.target)
|
|
1771
|
+
};
|
|
1772
|
+
const session = createCurrentSessionController({
|
|
1773
|
+
store: core.store,
|
|
1774
|
+
emitter: core.emitter,
|
|
1775
|
+
start: sessions.start,
|
|
1776
|
+
accept: sessions.accept,
|
|
1777
|
+
join: sessions.join,
|
|
1778
|
+
leave: sessions.leave
|
|
1779
|
+
});
|
|
1243
1780
|
return {
|
|
1244
1781
|
kind: "session",
|
|
1245
1782
|
get status() {
|
|
1246
1783
|
return core.store.getStatus();
|
|
1247
1784
|
},
|
|
1248
|
-
sessions
|
|
1249
|
-
...createCommonSessions(core),
|
|
1250
|
-
start: (input) => startSession(core, input.vendor, input.target)
|
|
1251
|
-
},
|
|
1785
|
+
sessions,
|
|
1252
1786
|
invites: {
|
|
1253
1787
|
list: core.store.invites,
|
|
1254
1788
|
get: core.store.invite
|
|
1255
1789
|
},
|
|
1790
|
+
session,
|
|
1256
1791
|
connect: core.connect,
|
|
1257
1792
|
disconnect: core.realtime.disconnect,
|
|
1258
|
-
dispose:
|
|
1793
|
+
dispose: async () => {
|
|
1794
|
+
session.dispose();
|
|
1795
|
+
await core.dispose();
|
|
1796
|
+
},
|
|
1259
1797
|
on: core.emitter.on,
|
|
1260
1798
|
subscribe: core.store.subscribe
|
|
1261
1799
|
};
|
|
1262
1800
|
};
|
|
1263
1801
|
var createHostClient = (config) => {
|
|
1264
1802
|
const core = createCore(config);
|
|
1803
|
+
const sessions = {
|
|
1804
|
+
...createCommonSessions(core),
|
|
1805
|
+
start: (input) => startSession(core, config.vendor, input.target)
|
|
1806
|
+
};
|
|
1807
|
+
const host = createHostControls(core);
|
|
1808
|
+
const session = createCurrentSessionController({
|
|
1809
|
+
store: core.store,
|
|
1810
|
+
emitter: core.emitter,
|
|
1811
|
+
start: sessions.start,
|
|
1812
|
+
accept: sessions.accept,
|
|
1813
|
+
join: sessions.join,
|
|
1814
|
+
leave: sessions.leave,
|
|
1815
|
+
end: host.end
|
|
1816
|
+
});
|
|
1265
1817
|
return {
|
|
1266
1818
|
kind: "host",
|
|
1267
1819
|
vendor: config.vendor,
|
|
1268
1820
|
get status() {
|
|
1269
1821
|
return core.store.getStatus();
|
|
1270
1822
|
},
|
|
1271
|
-
sessions
|
|
1272
|
-
...createCommonSessions(core),
|
|
1273
|
-
start: (input) => startSession(core, config.vendor, input.target)
|
|
1274
|
-
},
|
|
1823
|
+
sessions,
|
|
1275
1824
|
invites: {
|
|
1276
1825
|
list: core.store.invites,
|
|
1277
1826
|
get: core.store.invite
|
|
1278
1827
|
},
|
|
1279
|
-
|
|
1828
|
+
session,
|
|
1829
|
+
host,
|
|
1280
1830
|
connect: core.connect,
|
|
1281
1831
|
disconnect: core.realtime.disconnect,
|
|
1282
|
-
dispose:
|
|
1832
|
+
dispose: async () => {
|
|
1833
|
+
session.dispose();
|
|
1834
|
+
await core.dispose();
|
|
1835
|
+
},
|
|
1283
1836
|
on: core.emitter.on,
|
|
1284
1837
|
subscribe: core.store.subscribe
|
|
1285
1838
|
};
|
|
1286
1839
|
};
|
|
1840
|
+
var normalizePhoneTarget = (target) => {
|
|
1841
|
+
if (target.type === "phone") {
|
|
1842
|
+
return { ...target, phone: normalizeE164Phone(target.phone) };
|
|
1843
|
+
}
|
|
1844
|
+
return target;
|
|
1845
|
+
};
|
|
1287
1846
|
var createCore = (config) => {
|
|
1288
1847
|
const api = apiClient(config);
|
|
1289
1848
|
const store = createSessionStore(config.userId);
|
|
1290
1849
|
const emitter = createEmitter();
|
|
1291
|
-
const
|
|
1850
|
+
const syncActive = async () => {
|
|
1851
|
+
const query = { state: "active" };
|
|
1852
|
+
const response = await unwrap(listSessions({ client: api, query }), config.auth);
|
|
1853
|
+
store.sync(response.items, {
|
|
1854
|
+
mode: "replaceVisible",
|
|
1855
|
+
shouldReplace: (session) => matchesVisibleQuery(session, query)
|
|
1856
|
+
});
|
|
1857
|
+
};
|
|
1858
|
+
const realtime = createRealtime(api, config.auth, store, emitter, syncActive);
|
|
1292
1859
|
const applySession = (session, mode = "event") => {
|
|
1293
1860
|
const change = store.upsert(session, mode);
|
|
1294
1861
|
if (change === "added") {
|
|
@@ -1318,6 +1885,10 @@ var createCore = (config) => {
|
|
|
1318
1885
|
throw err;
|
|
1319
1886
|
}
|
|
1320
1887
|
};
|
|
1888
|
+
const dispose = async () => {
|
|
1889
|
+
await realtime.dispose();
|
|
1890
|
+
store.dispose();
|
|
1891
|
+
};
|
|
1321
1892
|
return {
|
|
1322
1893
|
api,
|
|
1323
1894
|
auth: config.auth,
|
|
@@ -1325,6 +1896,7 @@ var createCore = (config) => {
|
|
|
1325
1896
|
emitter,
|
|
1326
1897
|
realtime,
|
|
1327
1898
|
connect,
|
|
1899
|
+
dispose,
|
|
1328
1900
|
applySession,
|
|
1329
1901
|
applyStarted,
|
|
1330
1902
|
patchRecording
|
|
@@ -1333,9 +1905,12 @@ var createCore = (config) => {
|
|
|
1333
1905
|
var createCommonSessions = (core) => ({
|
|
1334
1906
|
list: core.store.list,
|
|
1335
1907
|
get: core.store.get,
|
|
1336
|
-
sync: async (query) => {
|
|
1908
|
+
sync: async (query, options) => {
|
|
1337
1909
|
const response = await unwrap(listSessions({ client: core.api, query }), core.auth);
|
|
1338
|
-
core.store.sync(response.items
|
|
1910
|
+
core.store.sync(response.items, {
|
|
1911
|
+
mode: options?.mode,
|
|
1912
|
+
shouldReplace: options?.mode === "replaceVisible" && canReplaceVisibleQuery(query) ? (session) => matchesVisibleQuery(session, query) : void 0
|
|
1913
|
+
});
|
|
1339
1914
|
return core.store.list();
|
|
1340
1915
|
},
|
|
1341
1916
|
join: (sessionId) => unwrap(createSessionToken({ client: core.api, path: { sessionId } }), core.auth),
|
|
@@ -1383,7 +1958,7 @@ var createHostControls = (core) => ({
|
|
|
1383
1958
|
addSessionParticipant({
|
|
1384
1959
|
client: core.api,
|
|
1385
1960
|
path: { sessionId: input.sessionId },
|
|
1386
|
-
body: { target: input.target }
|
|
1961
|
+
body: { target: normalizePhoneTarget(input.target) }
|
|
1387
1962
|
}),
|
|
1388
1963
|
core.auth
|
|
1389
1964
|
),
|
|
@@ -1414,12 +1989,28 @@ var startSession = async (core, vendor, target) => core.applyStarted(
|
|
|
1414
1989
|
createSession({
|
|
1415
1990
|
client: core.api,
|
|
1416
1991
|
path: { vendor },
|
|
1417
|
-
body: { target }
|
|
1992
|
+
body: { target: normalizePhoneTarget(target) }
|
|
1418
1993
|
}),
|
|
1419
1994
|
core.auth
|
|
1420
1995
|
),
|
|
1421
1996
|
"command"
|
|
1422
1997
|
);
|
|
1998
|
+
var matchesVisibleQuery = (session, query) => {
|
|
1999
|
+
if (query?.vendor && session.vendor !== query.vendor) {
|
|
2000
|
+
return false;
|
|
2001
|
+
}
|
|
2002
|
+
const state = query?.state ?? "active";
|
|
2003
|
+
if (state === "all") {
|
|
2004
|
+
return true;
|
|
2005
|
+
}
|
|
2006
|
+
if (state === "ended") {
|
|
2007
|
+
return session.state === "ended";
|
|
2008
|
+
}
|
|
2009
|
+
return session.state !== "ended";
|
|
2010
|
+
};
|
|
2011
|
+
var canReplaceVisibleQuery = (query) => {
|
|
2012
|
+
return !query?.before && query?.limit === void 0;
|
|
2013
|
+
};
|
|
1423
2014
|
|
|
1424
2015
|
exports.CallpadError = CallpadError;
|
|
1425
2016
|
exports.createHostClient = createHostClient;
|