@pallasoft/ps-utilities 2.19.1 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/constants/index.d.ts +1 -0
- package/dist/common/constants/index.js +1 -0
- package/dist/common/constants/text-tidy-config.d.ts +8 -0
- package/dist/common/constants/text-tidy-config.js +11 -0
- package/dist/common/index.d.ts +1 -0
- package/dist/common/index.js +1 -0
- package/dist/common/text-tidy-service.d.ts +4 -0
- package/dist/common/text-tidy-service.js +23 -0
- package/dist/esm/common/constants/index.js +1 -0
- package/dist/esm/common/constants/text-tidy-config.js +8 -0
- package/dist/esm/common/index.js +1 -0
- package/dist/esm/common/text-tidy-service.js +16 -0
- package/dist/esm/models/socket-live-options.js +1 -0
- package/dist/esm/sockets/index.js +1 -0
- package/dist/esm/sockets/socket-live-config.js +8 -0
- package/dist/esm/sockets/socket-live-service.js +96 -0
- package/dist/models/socket-live-options.d.ts +8 -0
- package/dist/models/socket-live-options.js +2 -0
- package/dist/sockets/index.d.ts +2 -0
- package/dist/sockets/index.js +1 -0
- package/dist/sockets/socket-live-config.d.ts +8 -0
- package/dist/sockets/socket-live-config.js +11 -0
- package/dist/sockets/socket-live-service.d.ts +20 -0
- package/dist/sockets/socket-live-service.js +174 -0
- package/package.json +1 -1
|
@@ -33,3 +33,4 @@ __exportStar(require("./http-method"), exports);
|
|
|
33
33
|
__exportStar(require("./relative-time-config"), exports);
|
|
34
34
|
__exportStar(require("./request-client-config"), exports);
|
|
35
35
|
__exportStar(require("./portable-text-config"), exports);
|
|
36
|
+
__exportStar(require("./text-tidy-config"), exports);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TextTidyConfig = void 0;
|
|
4
|
+
exports.TextTidyConfig = {
|
|
5
|
+
emptyBrackets: /\(\s*\)|\[\s*\]|\{\s*\}/g,
|
|
6
|
+
lineEndSpace: /[^\S\n]+\n/g,
|
|
7
|
+
lineStartSpace: /\n[^\S\n]+/g,
|
|
8
|
+
manyBlankLines: /\n{3,}/g,
|
|
9
|
+
manySpaces: /[^\S\n]{2,}/g,
|
|
10
|
+
spaceBeforePunctuation: /[^\S\n]+([,.;:!?])/g,
|
|
11
|
+
};
|
package/dist/common/index.d.ts
CHANGED
package/dist/common/index.js
CHANGED
|
@@ -41,3 +41,4 @@ __exportStar(require("./relative-time-service"), exports);
|
|
|
41
41
|
__exportStar(require("./request-client-service"), exports);
|
|
42
42
|
__exportStar(require("./json-service"), exports);
|
|
43
43
|
__exportStar(require("./portable-text-service"), exports);
|
|
44
|
+
__exportStar(require("./text-tidy-service"), exports);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TextTidyService = void 0;
|
|
4
|
+
var text_tidy_config_1 = require("./constants/text-tidy-config");
|
|
5
|
+
var TextTidyService = /** @class */ (function () {
|
|
6
|
+
function TextTidyService() {
|
|
7
|
+
}
|
|
8
|
+
TextTidyService.run = function (text) {
|
|
9
|
+
return text
|
|
10
|
+
.replace(text_tidy_config_1.TextTidyConfig.emptyBrackets, '')
|
|
11
|
+
.replace(text_tidy_config_1.TextTidyConfig.manySpaces, ' ')
|
|
12
|
+
.replace(text_tidy_config_1.TextTidyConfig.spaceBeforePunctuation, '$1');
|
|
13
|
+
};
|
|
14
|
+
TextTidyService.tidy = function (text) {
|
|
15
|
+
return TextTidyService.run(text)
|
|
16
|
+
.replace(text_tidy_config_1.TextTidyConfig.lineEndSpace, '\n')
|
|
17
|
+
.replace(text_tidy_config_1.TextTidyConfig.lineStartSpace, '\n')
|
|
18
|
+
.replace(text_tidy_config_1.TextTidyConfig.manyBlankLines, '\n\n')
|
|
19
|
+
.trim();
|
|
20
|
+
};
|
|
21
|
+
return TextTidyService;
|
|
22
|
+
}());
|
|
23
|
+
exports.TextTidyService = TextTidyService;
|
package/dist/esm/common/index.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TextTidyConfig } from './constants/text-tidy-config.js';
|
|
2
|
+
export class TextTidyService {
|
|
3
|
+
static run(text) {
|
|
4
|
+
return text
|
|
5
|
+
.replace(TextTidyConfig.emptyBrackets, '')
|
|
6
|
+
.replace(TextTidyConfig.manySpaces, ' ')
|
|
7
|
+
.replace(TextTidyConfig.spaceBeforePunctuation, '$1');
|
|
8
|
+
}
|
|
9
|
+
static tidy(text) {
|
|
10
|
+
return TextTidyService.run(text)
|
|
11
|
+
.replace(TextTidyConfig.lineEndSpace, '\n')
|
|
12
|
+
.replace(TextTidyConfig.lineStartSpace, '\n')
|
|
13
|
+
.replace(TextTidyConfig.manyBlankLines, '\n\n')
|
|
14
|
+
.trim();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { SocketClientService } from './socket-client-service.js';
|
|
2
|
+
import { SocketLiveConfig } from './socket-live-config.js';
|
|
3
|
+
export class SocketLiveService {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
this.socket = null;
|
|
7
|
+
this.token = null;
|
|
8
|
+
this.isClosed = false;
|
|
9
|
+
this.hasConnected = false;
|
|
10
|
+
this.failures = 0;
|
|
11
|
+
this.retryTimer = null;
|
|
12
|
+
this.hiddenTimer = null;
|
|
13
|
+
this.visibility = () => {
|
|
14
|
+
if (this.isClosed || this.socket === null)
|
|
15
|
+
return;
|
|
16
|
+
if (document.visibilityState === 'hidden') {
|
|
17
|
+
const delay = this.options.hiddenDelayMilliseconds === undefined
|
|
18
|
+
? SocketLiveConfig.hiddenDelayMilliseconds
|
|
19
|
+
: this.options.hiddenDelayMilliseconds;
|
|
20
|
+
this.hiddenTimer = setTimeout(() => {
|
|
21
|
+
this.hiddenTimer = null;
|
|
22
|
+
if (this.socket !== null)
|
|
23
|
+
this.socket.disconnect();
|
|
24
|
+
}, delay);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (this.hiddenTimer !== null) {
|
|
28
|
+
clearTimeout(this.hiddenTimer);
|
|
29
|
+
this.hiddenTimer = null;
|
|
30
|
+
}
|
|
31
|
+
if (!this.socket.connected)
|
|
32
|
+
void this.reconnect();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
static async open(options) {
|
|
36
|
+
const live = new SocketLiveService(options);
|
|
37
|
+
await live.start();
|
|
38
|
+
return live;
|
|
39
|
+
}
|
|
40
|
+
async start() {
|
|
41
|
+
this.token = await this.options.fetchToken();
|
|
42
|
+
if (this.isClosed || this.token === null)
|
|
43
|
+
return;
|
|
44
|
+
const socket = SocketClientService.connect({
|
|
45
|
+
path: this.options.path,
|
|
46
|
+
token: () => this.token,
|
|
47
|
+
});
|
|
48
|
+
this.socket = socket;
|
|
49
|
+
this.options.events.forEach((name) => socket.on(name, (payload) => this.options.onEvent(name, payload)));
|
|
50
|
+
socket.on(SocketLiveConfig.connectEvent, () => this.connected());
|
|
51
|
+
socket.on(SocketLiveConfig.connectErrorEvent, () => this.retryLater());
|
|
52
|
+
if (typeof document !== 'undefined')
|
|
53
|
+
document.addEventListener(SocketLiveConfig.visibilityEvent, this.visibility);
|
|
54
|
+
}
|
|
55
|
+
close() {
|
|
56
|
+
this.isClosed = true;
|
|
57
|
+
this.clearTimers();
|
|
58
|
+
if (typeof document !== 'undefined')
|
|
59
|
+
document.removeEventListener(SocketLiveConfig.visibilityEvent, this.visibility);
|
|
60
|
+
if (this.socket !== null)
|
|
61
|
+
this.socket.disconnect();
|
|
62
|
+
}
|
|
63
|
+
connected() {
|
|
64
|
+
this.failures = 0;
|
|
65
|
+
if (this.hasConnected && this.options.onResume !== undefined)
|
|
66
|
+
this.options.onResume();
|
|
67
|
+
this.hasConnected = true;
|
|
68
|
+
}
|
|
69
|
+
retryLater() {
|
|
70
|
+
if (this.isClosed || this.retryTimer !== null)
|
|
71
|
+
return;
|
|
72
|
+
const delay = Math.min(SocketLiveConfig.retryBaseMilliseconds * 2 ** this.failures, SocketLiveConfig.maxRetryMilliseconds);
|
|
73
|
+
this.failures += 1;
|
|
74
|
+
this.retryTimer = setTimeout(() => {
|
|
75
|
+
this.retryTimer = null;
|
|
76
|
+
void this.reconnect();
|
|
77
|
+
}, delay);
|
|
78
|
+
}
|
|
79
|
+
async reconnect() {
|
|
80
|
+
const token = await this.options.fetchToken();
|
|
81
|
+
if (this.isClosed || this.socket === null)
|
|
82
|
+
return;
|
|
83
|
+
if (token === null)
|
|
84
|
+
return this.retryLater();
|
|
85
|
+
this.token = token;
|
|
86
|
+
this.socket.connect();
|
|
87
|
+
}
|
|
88
|
+
clearTimers() {
|
|
89
|
+
if (this.retryTimer !== null)
|
|
90
|
+
clearTimeout(this.retryTimer);
|
|
91
|
+
if (this.hiddenTimer !== null)
|
|
92
|
+
clearTimeout(this.hiddenTimer);
|
|
93
|
+
this.retryTimer = null;
|
|
94
|
+
this.hiddenTimer = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
package/dist/sockets/index.d.ts
CHANGED
package/dist/sockets/index.js
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const SocketLiveConfig: {
|
|
2
|
+
readonly connectErrorEvent: "connect_error";
|
|
3
|
+
readonly connectEvent: "connect";
|
|
4
|
+
readonly hiddenDelayMilliseconds: 60000;
|
|
5
|
+
readonly maxRetryMilliseconds: 30000;
|
|
6
|
+
readonly retryBaseMilliseconds: 1000;
|
|
7
|
+
readonly visibilityEvent: "visibilitychange";
|
|
8
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SocketLiveConfig = void 0;
|
|
4
|
+
exports.SocketLiveConfig = {
|
|
5
|
+
connectErrorEvent: 'connect_error',
|
|
6
|
+
connectEvent: 'connect',
|
|
7
|
+
hiddenDelayMilliseconds: 60000,
|
|
8
|
+
maxRetryMilliseconds: 30000,
|
|
9
|
+
retryBaseMilliseconds: 1000,
|
|
10
|
+
visibilityEvent: 'visibilitychange',
|
|
11
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SocketLiveOptions } from '../models/socket-live-options';
|
|
2
|
+
export declare class SocketLiveService {
|
|
3
|
+
private readonly options;
|
|
4
|
+
private socket;
|
|
5
|
+
private token;
|
|
6
|
+
private isClosed;
|
|
7
|
+
private hasConnected;
|
|
8
|
+
private failures;
|
|
9
|
+
private retryTimer;
|
|
10
|
+
private hiddenTimer;
|
|
11
|
+
constructor(options: SocketLiveOptions);
|
|
12
|
+
static open(options: SocketLiveOptions): Promise<SocketLiveService>;
|
|
13
|
+
start(): Promise<void>;
|
|
14
|
+
close(): void;
|
|
15
|
+
private connected;
|
|
16
|
+
private retryLater;
|
|
17
|
+
private reconnect;
|
|
18
|
+
private readonly visibility;
|
|
19
|
+
private clearTimers;
|
|
20
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.SocketLiveService = void 0;
|
|
40
|
+
var socket_client_service_1 = require("./socket-client-service");
|
|
41
|
+
var socket_live_config_1 = require("./socket-live-config");
|
|
42
|
+
var SocketLiveService = /** @class */ (function () {
|
|
43
|
+
function SocketLiveService(options) {
|
|
44
|
+
var _this = this;
|
|
45
|
+
this.options = options;
|
|
46
|
+
this.socket = null;
|
|
47
|
+
this.token = null;
|
|
48
|
+
this.isClosed = false;
|
|
49
|
+
this.hasConnected = false;
|
|
50
|
+
this.failures = 0;
|
|
51
|
+
this.retryTimer = null;
|
|
52
|
+
this.hiddenTimer = null;
|
|
53
|
+
this.visibility = function () {
|
|
54
|
+
if (_this.isClosed || _this.socket === null)
|
|
55
|
+
return;
|
|
56
|
+
if (document.visibilityState === 'hidden') {
|
|
57
|
+
var delay = _this.options.hiddenDelayMilliseconds === undefined
|
|
58
|
+
? socket_live_config_1.SocketLiveConfig.hiddenDelayMilliseconds
|
|
59
|
+
: _this.options.hiddenDelayMilliseconds;
|
|
60
|
+
_this.hiddenTimer = setTimeout(function () {
|
|
61
|
+
_this.hiddenTimer = null;
|
|
62
|
+
if (_this.socket !== null)
|
|
63
|
+
_this.socket.disconnect();
|
|
64
|
+
}, delay);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (_this.hiddenTimer !== null) {
|
|
68
|
+
clearTimeout(_this.hiddenTimer);
|
|
69
|
+
_this.hiddenTimer = null;
|
|
70
|
+
}
|
|
71
|
+
if (!_this.socket.connected)
|
|
72
|
+
void _this.reconnect();
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
SocketLiveService.open = function (options) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
77
|
+
var live;
|
|
78
|
+
return __generator(this, function (_a) {
|
|
79
|
+
switch (_a.label) {
|
|
80
|
+
case 0:
|
|
81
|
+
live = new SocketLiveService(options);
|
|
82
|
+
return [4 /*yield*/, live.start()];
|
|
83
|
+
case 1:
|
|
84
|
+
_a.sent();
|
|
85
|
+
return [2 /*return*/, live];
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
SocketLiveService.prototype.start = function () {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
92
|
+
var _a, socket;
|
|
93
|
+
var _this = this;
|
|
94
|
+
return __generator(this, function (_b) {
|
|
95
|
+
switch (_b.label) {
|
|
96
|
+
case 0:
|
|
97
|
+
_a = this;
|
|
98
|
+
return [4 /*yield*/, this.options.fetchToken()];
|
|
99
|
+
case 1:
|
|
100
|
+
_a.token = _b.sent();
|
|
101
|
+
if (this.isClosed || this.token === null)
|
|
102
|
+
return [2 /*return*/];
|
|
103
|
+
socket = socket_client_service_1.SocketClientService.connect({
|
|
104
|
+
path: this.options.path,
|
|
105
|
+
token: function () { return _this.token; },
|
|
106
|
+
});
|
|
107
|
+
this.socket = socket;
|
|
108
|
+
this.options.events.forEach(function (name) {
|
|
109
|
+
return socket.on(name, function (payload) { return _this.options.onEvent(name, payload); });
|
|
110
|
+
});
|
|
111
|
+
socket.on(socket_live_config_1.SocketLiveConfig.connectEvent, function () { return _this.connected(); });
|
|
112
|
+
socket.on(socket_live_config_1.SocketLiveConfig.connectErrorEvent, function () { return _this.retryLater(); });
|
|
113
|
+
if (typeof document !== 'undefined')
|
|
114
|
+
document.addEventListener(socket_live_config_1.SocketLiveConfig.visibilityEvent, this.visibility);
|
|
115
|
+
return [2 /*return*/];
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
SocketLiveService.prototype.close = function () {
|
|
121
|
+
this.isClosed = true;
|
|
122
|
+
this.clearTimers();
|
|
123
|
+
if (typeof document !== 'undefined')
|
|
124
|
+
document.removeEventListener(socket_live_config_1.SocketLiveConfig.visibilityEvent, this.visibility);
|
|
125
|
+
if (this.socket !== null)
|
|
126
|
+
this.socket.disconnect();
|
|
127
|
+
};
|
|
128
|
+
SocketLiveService.prototype.connected = function () {
|
|
129
|
+
this.failures = 0;
|
|
130
|
+
if (this.hasConnected && this.options.onResume !== undefined)
|
|
131
|
+
this.options.onResume();
|
|
132
|
+
this.hasConnected = true;
|
|
133
|
+
};
|
|
134
|
+
SocketLiveService.prototype.retryLater = function () {
|
|
135
|
+
var _this = this;
|
|
136
|
+
if (this.isClosed || this.retryTimer !== null)
|
|
137
|
+
return;
|
|
138
|
+
var delay = Math.min(socket_live_config_1.SocketLiveConfig.retryBaseMilliseconds * Math.pow(2, this.failures), socket_live_config_1.SocketLiveConfig.maxRetryMilliseconds);
|
|
139
|
+
this.failures += 1;
|
|
140
|
+
this.retryTimer = setTimeout(function () {
|
|
141
|
+
_this.retryTimer = null;
|
|
142
|
+
void _this.reconnect();
|
|
143
|
+
}, delay);
|
|
144
|
+
};
|
|
145
|
+
SocketLiveService.prototype.reconnect = function () {
|
|
146
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
147
|
+
var token;
|
|
148
|
+
return __generator(this, function (_a) {
|
|
149
|
+
switch (_a.label) {
|
|
150
|
+
case 0: return [4 /*yield*/, this.options.fetchToken()];
|
|
151
|
+
case 1:
|
|
152
|
+
token = _a.sent();
|
|
153
|
+
if (this.isClosed || this.socket === null)
|
|
154
|
+
return [2 /*return*/];
|
|
155
|
+
if (token === null)
|
|
156
|
+
return [2 /*return*/, this.retryLater()];
|
|
157
|
+
this.token = token;
|
|
158
|
+
this.socket.connect();
|
|
159
|
+
return [2 /*return*/];
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
SocketLiveService.prototype.clearTimers = function () {
|
|
165
|
+
if (this.retryTimer !== null)
|
|
166
|
+
clearTimeout(this.retryTimer);
|
|
167
|
+
if (this.hiddenTimer !== null)
|
|
168
|
+
clearTimeout(this.hiddenTimer);
|
|
169
|
+
this.retryTimer = null;
|
|
170
|
+
this.hiddenTimer = null;
|
|
171
|
+
};
|
|
172
|
+
return SocketLiveService;
|
|
173
|
+
}());
|
|
174
|
+
exports.SocketLiveService = SocketLiveService;
|