@absolutejs/voice 0.0.22-beta.189 → 0.0.22-beta.190
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/angular/index.d.ts +1 -0
- package/dist/angular/index.js +277 -148
- package/dist/angular/voice-live-ops.service.d.ts +11 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +1385 -105
- package/dist/client/liveOps.d.ts +22 -0
- package/dist/client/liveOpsWidget.d.ts +23 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +252 -59
- package/dist/liveOps.d.ts +122 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +119 -16
- package/dist/react/useVoiceLiveOps.d.ts +9 -0
- package/dist/svelte/createVoiceLiveOps.d.ts +13 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +1389 -101
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +165 -44
- package/dist/vue/useVoiceLiveOps.d.ts +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
2
|
+
import { type VoiceAuditEventStore } from './audit';
|
|
3
|
+
import { type VoiceTraceEventStore } from './trace';
|
|
4
|
+
export declare const VOICE_LIVE_OPS_ACTIONS: readonly ["assign", "create-task", "escalate", "force-handoff", "inject-instruction", "operator-takeover", "pause-assistant", "resume-assistant", "tag"];
|
|
5
|
+
export type VoiceLiveOpsAction = (typeof VOICE_LIVE_OPS_ACTIONS)[number];
|
|
6
|
+
export type VoiceLiveOpsControlStatus = 'assistant-paused' | 'assistant-resumed' | 'handoff-forced' | 'instruction-injected' | 'operator-takeover' | 'recorded';
|
|
7
|
+
export type VoiceLiveOpsControlState = {
|
|
8
|
+
assistantPaused: boolean;
|
|
9
|
+
handoffTarget?: string;
|
|
10
|
+
injectedInstruction?: string;
|
|
11
|
+
lastAction: VoiceLiveOpsAction;
|
|
12
|
+
lastUpdatedAt: number;
|
|
13
|
+
operator?: string;
|
|
14
|
+
operatorTakeover: boolean;
|
|
15
|
+
status: VoiceLiveOpsControlStatus;
|
|
16
|
+
tag?: string;
|
|
17
|
+
};
|
|
18
|
+
export type VoiceLiveOpsActionInput = {
|
|
19
|
+
action: VoiceLiveOpsAction;
|
|
20
|
+
assignee?: string;
|
|
21
|
+
detail?: string;
|
|
22
|
+
sessionId: string;
|
|
23
|
+
tag?: string;
|
|
24
|
+
};
|
|
25
|
+
export type VoiceLiveOpsActionResult = {
|
|
26
|
+
action: VoiceLiveOpsAction;
|
|
27
|
+
control: VoiceLiveOpsControlState;
|
|
28
|
+
ok: true;
|
|
29
|
+
sessionId: string;
|
|
30
|
+
};
|
|
31
|
+
export type VoiceLiveOpsControlStore = {
|
|
32
|
+
get: (sessionId: string) => Promise<VoiceLiveOpsControlState | undefined> | VoiceLiveOpsControlState | undefined;
|
|
33
|
+
set: (sessionId: string, state: VoiceLiveOpsControlState) => Promise<void> | void;
|
|
34
|
+
};
|
|
35
|
+
export type VoiceLiveOpsControllerOptions = {
|
|
36
|
+
audit?: VoiceAuditEventStore;
|
|
37
|
+
defaultAssignee?: string;
|
|
38
|
+
defaultDetail?: string;
|
|
39
|
+
defaultTag?: string;
|
|
40
|
+
onAction?: (result: VoiceLiveOpsActionResult & {
|
|
41
|
+
assignee: string;
|
|
42
|
+
detail: string;
|
|
43
|
+
tag: string;
|
|
44
|
+
}) => Promise<void> | void;
|
|
45
|
+
store?: VoiceLiveOpsControlStore;
|
|
46
|
+
trace?: VoiceTraceEventStore;
|
|
47
|
+
};
|
|
48
|
+
export type VoiceLiveOpsRoutesOptions = VoiceLiveOpsControllerOptions & {
|
|
49
|
+
controlPath?: string;
|
|
50
|
+
name?: string;
|
|
51
|
+
path?: string;
|
|
52
|
+
};
|
|
53
|
+
export declare const createVoiceMemoryLiveOpsControlStore: () => VoiceLiveOpsControlStore;
|
|
54
|
+
export declare const getVoiceLiveOpsControlStatus: (action: VoiceLiveOpsAction) => VoiceLiveOpsControlStatus;
|
|
55
|
+
export declare const buildVoiceLiveOpsControlState: (input: VoiceLiveOpsActionInput & {
|
|
56
|
+
at?: number;
|
|
57
|
+
previous?: VoiceLiveOpsControlState;
|
|
58
|
+
}) => VoiceLiveOpsControlState;
|
|
59
|
+
export declare const createVoiceLiveOpsController: (options?: VoiceLiveOpsControllerOptions) => {
|
|
60
|
+
get: (sessionId: string) => VoiceLiveOpsControlState | Promise<VoiceLiveOpsControlState | undefined> | undefined;
|
|
61
|
+
perform: (input: VoiceLiveOpsActionInput) => Promise<VoiceLiveOpsActionResult>;
|
|
62
|
+
store: VoiceLiveOpsControlStore;
|
|
63
|
+
};
|
|
64
|
+
export declare const createVoiceLiveOpsRoutes: (options?: VoiceLiveOpsRoutesOptions) => Elysia<"", {
|
|
65
|
+
decorator: {};
|
|
66
|
+
store: {};
|
|
67
|
+
derive: {};
|
|
68
|
+
resolve: {};
|
|
69
|
+
}, {
|
|
70
|
+
typebox: {};
|
|
71
|
+
error: {};
|
|
72
|
+
}, {
|
|
73
|
+
schema: {};
|
|
74
|
+
standaloneSchema: {};
|
|
75
|
+
macro: {};
|
|
76
|
+
macroFn: {};
|
|
77
|
+
parser: {};
|
|
78
|
+
response: {};
|
|
79
|
+
}, {
|
|
80
|
+
[x: string]: {
|
|
81
|
+
post: {
|
|
82
|
+
body: unknown;
|
|
83
|
+
params: {};
|
|
84
|
+
query: unknown;
|
|
85
|
+
headers: unknown;
|
|
86
|
+
response: {
|
|
87
|
+
200: VoiceLiveOpsActionResult | {
|
|
88
|
+
error: string;
|
|
89
|
+
ok: boolean;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
} & {
|
|
95
|
+
[x: string]: {
|
|
96
|
+
get: {
|
|
97
|
+
body: unknown;
|
|
98
|
+
params: {};
|
|
99
|
+
query: unknown;
|
|
100
|
+
headers: unknown;
|
|
101
|
+
response: {
|
|
102
|
+
200: {
|
|
103
|
+
control: VoiceLiveOpsControlState | undefined;
|
|
104
|
+
ok: boolean;
|
|
105
|
+
sessionId: string;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
}, {
|
|
111
|
+
derive: {};
|
|
112
|
+
resolve: {};
|
|
113
|
+
schema: {};
|
|
114
|
+
standaloneSchema: {};
|
|
115
|
+
response: {};
|
|
116
|
+
}, {
|
|
117
|
+
derive: {};
|
|
118
|
+
resolve: {};
|
|
119
|
+
schema: {};
|
|
120
|
+
standaloneSchema: {};
|
|
121
|
+
response: {};
|
|
122
|
+
}>;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { VoiceTurnLatency } from './VoiceTurnLatency';
|
|
|
11
11
|
export { VoiceTurnQuality } from './VoiceTurnQuality';
|
|
12
12
|
export { useVoiceOpsStatus } from './useVoiceOpsStatus';
|
|
13
13
|
export { useVoiceOpsActionCenter } from './useVoiceOpsActionCenter';
|
|
14
|
+
export { useVoiceLiveOps } from './useVoiceLiveOps';
|
|
14
15
|
export { useVoiceDeliveryRuntime } from './useVoiceDeliveryRuntime';
|
|
15
16
|
export { useVoiceCampaignDialerProof } from './useVoiceCampaignDialerProof';
|
|
16
17
|
export { useVoiceStream } from './useVoiceStream';
|
package/dist/react/index.js
CHANGED
|
@@ -3401,9 +3401,111 @@ var VoiceTurnQuality = ({
|
|
|
3401
3401
|
]
|
|
3402
3402
|
}, undefined, true, undefined, this);
|
|
3403
3403
|
};
|
|
3404
|
-
// src/react/
|
|
3404
|
+
// src/react/useVoiceLiveOps.tsx
|
|
3405
3405
|
import { useEffect as useEffect12, useRef as useRef12, useSyncExternalStore as useSyncExternalStore12 } from "react";
|
|
3406
3406
|
|
|
3407
|
+
// src/client/liveOps.ts
|
|
3408
|
+
var postVoiceLiveOpsAction = async (input, options = {}) => {
|
|
3409
|
+
if (!input.sessionId) {
|
|
3410
|
+
throw new Error("Start a voice session before running live ops actions.");
|
|
3411
|
+
}
|
|
3412
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
3413
|
+
const response = await fetchImpl(options.actionPath ?? "/api/voice/live-ops/action", {
|
|
3414
|
+
body: JSON.stringify(input),
|
|
3415
|
+
headers: {
|
|
3416
|
+
"Content-Type": "application/json"
|
|
3417
|
+
},
|
|
3418
|
+
method: "POST"
|
|
3419
|
+
});
|
|
3420
|
+
const payload = await response.json().catch(() => null);
|
|
3421
|
+
if (!response.ok || !payload?.ok) {
|
|
3422
|
+
const message = payload && typeof payload === "object" && "error" in payload ? String(payload.error) : `Voice live ops action failed: HTTP ${response.status}`;
|
|
3423
|
+
throw new Error(message);
|
|
3424
|
+
}
|
|
3425
|
+
return payload;
|
|
3426
|
+
};
|
|
3427
|
+
var createVoiceLiveOpsStore = (options = {}) => {
|
|
3428
|
+
const listeners = new Set;
|
|
3429
|
+
let closed = false;
|
|
3430
|
+
let snapshot = {
|
|
3431
|
+
error: null,
|
|
3432
|
+
isRunning: false
|
|
3433
|
+
};
|
|
3434
|
+
const emit = () => {
|
|
3435
|
+
for (const listener of listeners) {
|
|
3436
|
+
listener();
|
|
3437
|
+
}
|
|
3438
|
+
};
|
|
3439
|
+
const run = async (input) => {
|
|
3440
|
+
if (closed) {
|
|
3441
|
+
return snapshot.lastResult;
|
|
3442
|
+
}
|
|
3443
|
+
snapshot = {
|
|
3444
|
+
...snapshot,
|
|
3445
|
+
error: null,
|
|
3446
|
+
isRunning: true,
|
|
3447
|
+
runningAction: input.action
|
|
3448
|
+
};
|
|
3449
|
+
emit();
|
|
3450
|
+
try {
|
|
3451
|
+
const result = await postVoiceLiveOpsAction(input, options);
|
|
3452
|
+
await options.onControl?.(result);
|
|
3453
|
+
snapshot = {
|
|
3454
|
+
...snapshot,
|
|
3455
|
+
error: null,
|
|
3456
|
+
isRunning: false,
|
|
3457
|
+
lastResult: result,
|
|
3458
|
+
runningAction: undefined,
|
|
3459
|
+
updatedAt: Date.now()
|
|
3460
|
+
};
|
|
3461
|
+
emit();
|
|
3462
|
+
return result;
|
|
3463
|
+
} catch (error) {
|
|
3464
|
+
snapshot = {
|
|
3465
|
+
...snapshot,
|
|
3466
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3467
|
+
isRunning: false,
|
|
3468
|
+
runningAction: undefined,
|
|
3469
|
+
updatedAt: Date.now()
|
|
3470
|
+
};
|
|
3471
|
+
emit();
|
|
3472
|
+
throw error;
|
|
3473
|
+
}
|
|
3474
|
+
};
|
|
3475
|
+
const close = () => {
|
|
3476
|
+
closed = true;
|
|
3477
|
+
listeners.clear();
|
|
3478
|
+
};
|
|
3479
|
+
return {
|
|
3480
|
+
close,
|
|
3481
|
+
getServerSnapshot: () => snapshot,
|
|
3482
|
+
getSnapshot: () => snapshot,
|
|
3483
|
+
run,
|
|
3484
|
+
subscribe: (listener) => {
|
|
3485
|
+
listeners.add(listener);
|
|
3486
|
+
return () => {
|
|
3487
|
+
listeners.delete(listener);
|
|
3488
|
+
};
|
|
3489
|
+
}
|
|
3490
|
+
};
|
|
3491
|
+
};
|
|
3492
|
+
|
|
3493
|
+
// src/react/useVoiceLiveOps.tsx
|
|
3494
|
+
var useVoiceLiveOps = (options = {}) => {
|
|
3495
|
+
const storeRef = useRef12(null);
|
|
3496
|
+
if (!storeRef.current) {
|
|
3497
|
+
storeRef.current = createVoiceLiveOpsStore(options);
|
|
3498
|
+
}
|
|
3499
|
+
const store = storeRef.current;
|
|
3500
|
+
useEffect12(() => () => store.close(), [store]);
|
|
3501
|
+
return {
|
|
3502
|
+
...useSyncExternalStore12(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
3503
|
+
run: store.run
|
|
3504
|
+
};
|
|
3505
|
+
};
|
|
3506
|
+
// src/react/useVoiceCampaignDialerProof.tsx
|
|
3507
|
+
import { useEffect as useEffect13, useRef as useRef13, useSyncExternalStore as useSyncExternalStore13 } from "react";
|
|
3508
|
+
|
|
3407
3509
|
// src/client/campaignDialerProof.ts
|
|
3408
3510
|
var fetchVoiceCampaignDialerProofStatus = async (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
|
|
3409
3511
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -3524,23 +3626,23 @@ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-p
|
|
|
3524
3626
|
|
|
3525
3627
|
// src/react/useVoiceCampaignDialerProof.tsx
|
|
3526
3628
|
var useVoiceCampaignDialerProof = (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
|
|
3527
|
-
const storeRef =
|
|
3629
|
+
const storeRef = useRef13(null);
|
|
3528
3630
|
if (!storeRef.current) {
|
|
3529
3631
|
storeRef.current = createVoiceCampaignDialerProofStore(path, options);
|
|
3530
3632
|
}
|
|
3531
3633
|
const store = storeRef.current;
|
|
3532
|
-
|
|
3634
|
+
useEffect13(() => {
|
|
3533
3635
|
store.refresh().catch(() => {});
|
|
3534
3636
|
return () => store.close();
|
|
3535
3637
|
}, [store]);
|
|
3536
3638
|
return {
|
|
3537
|
-
...
|
|
3639
|
+
...useSyncExternalStore13(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
3538
3640
|
refresh: store.refresh,
|
|
3539
3641
|
runProof: store.runProof
|
|
3540
3642
|
};
|
|
3541
3643
|
};
|
|
3542
3644
|
// src/react/useVoiceStream.tsx
|
|
3543
|
-
import { useEffect as
|
|
3645
|
+
import { useEffect as useEffect14, useRef as useRef14, useSyncExternalStore as useSyncExternalStore14 } from "react";
|
|
3544
3646
|
|
|
3545
3647
|
// src/client/actions.ts
|
|
3546
3648
|
var normalizeErrorMessage = (value) => {
|
|
@@ -4200,13 +4302,13 @@ var EMPTY_SNAPSHOT = {
|
|
|
4200
4302
|
turns: []
|
|
4201
4303
|
};
|
|
4202
4304
|
var useVoiceStream = (path, options = {}) => {
|
|
4203
|
-
const streamRef =
|
|
4305
|
+
const streamRef = useRef14(null);
|
|
4204
4306
|
if (!streamRef.current) {
|
|
4205
4307
|
streamRef.current = createVoiceStream(path, options);
|
|
4206
4308
|
}
|
|
4207
4309
|
const stream = streamRef.current;
|
|
4208
|
-
|
|
4209
|
-
const snapshot =
|
|
4310
|
+
useEffect14(() => () => stream.close(), [stream]);
|
|
4311
|
+
const snapshot = useSyncExternalStore14(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
|
|
4210
4312
|
return {
|
|
4211
4313
|
...snapshot,
|
|
4212
4314
|
callControl: (message) => stream.callControl(message),
|
|
@@ -4216,7 +4318,7 @@ var useVoiceStream = (path, options = {}) => {
|
|
|
4216
4318
|
};
|
|
4217
4319
|
};
|
|
4218
4320
|
// src/react/useVoiceController.tsx
|
|
4219
|
-
import { useEffect as
|
|
4321
|
+
import { useEffect as useEffect15, useRef as useRef15, useSyncExternalStore as useSyncExternalStore15 } from "react";
|
|
4220
4322
|
|
|
4221
4323
|
// src/client/htmx.ts
|
|
4222
4324
|
var DEFAULT_EVENT_NAME = "voice-refresh";
|
|
@@ -4879,13 +4981,13 @@ var EMPTY_SNAPSHOT2 = {
|
|
|
4879
4981
|
turns: []
|
|
4880
4982
|
};
|
|
4881
4983
|
var useVoiceController = (path, options = {}) => {
|
|
4882
|
-
const controllerRef =
|
|
4984
|
+
const controllerRef = useRef15(null);
|
|
4883
4985
|
if (!controllerRef.current) {
|
|
4884
4986
|
controllerRef.current = createVoiceController(path, options);
|
|
4885
4987
|
}
|
|
4886
4988
|
const controller = controllerRef.current;
|
|
4887
|
-
|
|
4888
|
-
const snapshot =
|
|
4989
|
+
useEffect15(() => () => controller.close(), [controller]);
|
|
4990
|
+
const snapshot = useSyncExternalStore15(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
|
|
4889
4991
|
return {
|
|
4890
4992
|
...snapshot,
|
|
4891
4993
|
bindHTMX: controller.bindHTMX,
|
|
@@ -4899,7 +5001,7 @@ var useVoiceController = (path, options = {}) => {
|
|
|
4899
5001
|
};
|
|
4900
5002
|
};
|
|
4901
5003
|
// src/react/useVoiceWorkflowStatus.tsx
|
|
4902
|
-
import { useEffect as
|
|
5004
|
+
import { useEffect as useEffect16, useRef as useRef16, useSyncExternalStore as useSyncExternalStore16 } from "react";
|
|
4903
5005
|
|
|
4904
5006
|
// src/client/workflowStatus.ts
|
|
4905
5007
|
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
@@ -4982,17 +5084,17 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
|
|
|
4982
5084
|
|
|
4983
5085
|
// src/react/useVoiceWorkflowStatus.tsx
|
|
4984
5086
|
var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
|
|
4985
|
-
const storeRef =
|
|
5087
|
+
const storeRef = useRef16(null);
|
|
4986
5088
|
if (!storeRef.current) {
|
|
4987
5089
|
storeRef.current = createVoiceWorkflowStatusStore(path, options);
|
|
4988
5090
|
}
|
|
4989
5091
|
const store = storeRef.current;
|
|
4990
|
-
|
|
5092
|
+
useEffect16(() => {
|
|
4991
5093
|
store.refresh().catch(() => {});
|
|
4992
5094
|
return () => store.close();
|
|
4993
5095
|
}, [store]);
|
|
4994
5096
|
return {
|
|
4995
|
-
...
|
|
5097
|
+
...useSyncExternalStore16(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
4996
5098
|
refresh: store.refresh
|
|
4997
5099
|
};
|
|
4998
5100
|
};
|
|
@@ -5009,6 +5111,7 @@ export {
|
|
|
5009
5111
|
useVoiceProviderCapabilities,
|
|
5010
5112
|
useVoiceOpsStatus,
|
|
5011
5113
|
useVoiceOpsActionCenter,
|
|
5114
|
+
useVoiceLiveOps,
|
|
5012
5115
|
useVoiceDeliveryRuntime,
|
|
5013
5116
|
useVoiceController,
|
|
5014
5117
|
useVoiceCampaignDialerProof,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type VoiceLiveOpsClientOptions } from '../client/liveOps';
|
|
2
|
+
export declare const useVoiceLiveOps: (options?: VoiceLiveOpsClientOptions) => {
|
|
3
|
+
run: (input: import("..").VoiceLiveOpsActionInput) => Promise<import("..").VoiceLiveOpsActionResult | undefined>;
|
|
4
|
+
error: string | null;
|
|
5
|
+
isRunning: boolean;
|
|
6
|
+
lastResult?: import("..").VoiceLiveOpsActionResult;
|
|
7
|
+
runningAction?: import("..").VoiceLiveOpsAction;
|
|
8
|
+
updatedAt?: number;
|
|
9
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type VoiceLiveOpsClientOptions } from '../client/liveOps';
|
|
2
|
+
import { type VoiceLiveOpsWidgetOptions } from '../client/liveOpsWidget';
|
|
3
|
+
export declare const createVoiceLiveOps: (options?: VoiceLiveOpsClientOptions | VoiceLiveOpsWidgetOptions) => {
|
|
4
|
+
close: () => void;
|
|
5
|
+
getHTML: () => string;
|
|
6
|
+
getSnapshot: () => import("../client").VoiceLiveOpsSnapshot;
|
|
7
|
+
mount: (element: Element) => {
|
|
8
|
+
close: () => void;
|
|
9
|
+
run: (input: import("..").VoiceLiveOpsActionInput) => Promise<import("..").VoiceLiveOpsActionResult | undefined>;
|
|
10
|
+
};
|
|
11
|
+
run: (input: import("..").VoiceLiveOpsActionInput) => Promise<import("..").VoiceLiveOpsActionResult | undefined>;
|
|
12
|
+
subscribe: (listener: () => void) => () => void;
|
|
13
|
+
};
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createVoiceCampaignDialerProof } from './createVoiceCampaignDialerProof';
|
|
2
2
|
export { createVoiceDeliveryRuntime } from './createVoiceDeliveryRuntime';
|
|
3
3
|
export { createVoiceOpsActionCenter } from './createVoiceOpsActionCenter';
|
|
4
|
+
export { createVoiceLiveOps } from './createVoiceLiveOps';
|
|
4
5
|
export { createVoiceOpsStatus } from './createVoiceOpsStatus';
|
|
5
6
|
export { createVoiceProviderSimulationControls } from './createVoiceProviderSimulationControls';
|
|
6
7
|
export { createVoiceProviderCapabilities } from './createVoiceProviderCapabilities';
|