@fonoster/sdk 0.26.0 → 0.30.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/README.md +6 -5
- package/dist/node/Applications.d.ts +21 -85
- package/dist/node/Applications.js +55 -73
- package/dist/node/CallRecords.d.ts +92 -0
- package/dist/node/CallRecords.js +124 -0
- package/dist/node/Domains.js +4 -2
- package/dist/node/Providers.d.ts +49 -0
- package/dist/node/Providers.js +107 -0
- package/dist/node/Regions.d.ts +91 -0
- package/dist/node/Regions.js +124 -0
- package/dist/node/Users.d.ts +4 -2
- package/dist/node/Users.js +4 -2
- package/dist/node/Webhooks.d.ts +56 -0
- package/dist/node/Webhooks.js +139 -0
- package/dist/node/Workspaces.d.ts +22 -3
- package/dist/node/Workspaces.js +29 -2
- package/dist/node/client/AbstractClient.d.ts +83 -3
- package/dist/node/client/AbstractClient.js +97 -1
- package/dist/node/client/Client.d.ts +7 -0
- package/dist/node/client/Client.js +21 -0
- package/dist/node/client/jsonToObject.js +17 -0
- package/dist/node/client/objectToJson.js +35 -0
- package/dist/node/client/types/ApplicationsClient.d.ts +2 -1
- package/dist/node/client/types/CapabilitiesClient.d.ts +24 -0
- package/dist/node/client/types/CapabilitiesClient.js +2 -0
- package/dist/node/client/types/FonosterClient.d.ts +8 -0
- package/dist/node/client/types/IdentityClient.d.ts +3 -1
- package/dist/node/client/types/ProvidersClient.d.ts +28 -0
- package/dist/node/client/types/ProvidersClient.js +2 -0
- package/dist/node/client/types/WebhooksClient.d.ts +31 -0
- package/dist/node/client/types/WebhooksClient.js +2 -0
- package/dist/node/client/types/index.d.ts +3 -0
- package/dist/node/client/types/index.js +3 -0
- package/dist/node/generated/node/applications_grpc_pb.js +34 -0
- package/dist/node/generated/node/applications_pb.js +2657 -206
- package/dist/node/generated/node/capabilities_grpc_pb.js +66 -0
- package/dist/node/generated/node/capabilities_pb.js +2085 -0
- package/dist/node/generated/node/identity_grpc_pb.js +34 -0
- package/dist/node/generated/node/identity_pb.js +609 -5
- package/dist/node/generated/node/providers_grpc_pb.js +200 -0
- package/dist/node/generated/node/providers_pb.js +3034 -0
- package/dist/node/generated/node/webhooks_grpc_pb.js +302 -0
- package/dist/node/generated/node/webhooks_pb.js +3314 -0
- package/dist/node/generated/web/ApplicationsServiceClientPb.ts +43 -0
- package/dist/node/generated/web/CapabilitiesServiceClientPb.ts +87 -0
- package/dist/node/generated/web/IdentityServiceClientPb.ts +43 -0
- package/dist/node/generated/web/ProvidersServiceClientPb.ts +258 -0
- package/dist/node/generated/web/WebhooksServiceClientPb.ts +387 -0
- package/dist/node/generated/web/applications_pb.d.ts +301 -1
- package/dist/node/generated/web/applications_pb.js +2657 -206
- package/dist/node/generated/web/capabilities_pb.d.ts +249 -0
- package/dist/node/generated/web/capabilities_pb.js +2085 -0
- package/dist/node/generated/web/identity_pb.d.ts +76 -0
- package/dist/node/generated/web/identity_pb.js +609 -5
- package/dist/node/generated/web/providers_pb.d.ts +364 -0
- package/dist/node/generated/web/providers_pb.js +3034 -0
- package/dist/node/generated/web/webhooks_pb.d.ts +398 -0
- package/dist/node/generated/web/webhooks_pb.js +3314 -0
- package/dist/node/node.d.ts +5 -0
- package/dist/node/node.js +4 -0
- package/dist/node/tsconfig.tsbuildinfo +1 -1
- package/dist/node/utils.d.ts +1 -13
- package/dist/node/utils.js +1 -38
- package/dist/web/fonoster.min.js +1 -1
- package/dist/web/index.esm.js +1 -1
- package/package.json +5 -5
package/dist/node/Workspaces.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Workspaces = void 0;
|
|
4
4
|
const makeRpcRequest_1 = require("./client/makeRpcRequest");
|
|
5
5
|
const identity_pb_1 = require("./generated/node/identity_pb");
|
|
6
|
+
const Regions_1 = require("./Regions");
|
|
6
7
|
/**
|
|
7
8
|
* @classdesc Fonoster Workspaces, part of the Fonoster Identity subsystem,
|
|
8
9
|
* allows you to create, update, retrieve, and delete Workspaces in the system.
|
|
@@ -53,7 +54,8 @@ class Workspaces {
|
|
|
53
54
|
*
|
|
54
55
|
* @param {CreateWorkspaceRequest} request - The request object that contains the necessary information to create a new Workspace
|
|
55
56
|
* @param {string} request.name - The name of the Workspace
|
|
56
|
-
* @
|
|
57
|
+
* @param {string} request.region - An offered public region id (default `default`) or one of your private regions (id or ref, see `Regions.createRegion`); fixed for life
|
|
58
|
+
* @return {Promise<CreateWorkspaceResponse>} - The reference and the stored region
|
|
57
59
|
* @example
|
|
58
60
|
* const workspaces = new SDK.Workspaces(client); // Existing client object
|
|
59
61
|
*
|
|
@@ -68,12 +70,37 @@ class Workspaces {
|
|
|
68
70
|
*/
|
|
69
71
|
async createWorkspace(request) {
|
|
70
72
|
const client = this.client.getIdentityClient();
|
|
71
|
-
|
|
73
|
+
const res = await (0, makeRpcRequest_1.makeRpcRequest)({
|
|
72
74
|
method: client.createWorkspace.bind(client),
|
|
73
75
|
requestPBObjectConstructor: identity_pb_1.CreateWorkspaceRequest,
|
|
74
76
|
metadata: this.client.getMetadata(),
|
|
75
77
|
request
|
|
76
78
|
});
|
|
79
|
+
return { ref: res.ref, region: res.region || undefined };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The current workspace's region (add-private-regions): for a private region, the cell's status,
|
|
83
|
+
* version, components, active calls and which secrets it needs and has.
|
|
84
|
+
*
|
|
85
|
+
* @return {Promise<Region>} - The region view
|
|
86
|
+
*/
|
|
87
|
+
async getRegion() {
|
|
88
|
+
return new Regions_1.Regions(this.client).getRegion();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Issues a new one-time enrollment token for the current workspace's region (its owner only).
|
|
92
|
+
* Enrolling a cell with it revokes the previous cell's credentials.
|
|
93
|
+
*
|
|
94
|
+
* @return {Promise<RegionEnrollmentToken>} - The token (shown once) and its expiry
|
|
95
|
+
*/
|
|
96
|
+
async reissueRegionToken() {
|
|
97
|
+
return new Regions_1.Regions(this.client).reissueRegionToken();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Revokes the current workspace's region cell now; it goes back to `waiting` (region owner).
|
|
101
|
+
*/
|
|
102
|
+
async disconnectRegion() {
|
|
103
|
+
return new Regions_1.Regions(this.client).disconnectRegion();
|
|
77
104
|
}
|
|
78
105
|
/**
|
|
79
106
|
* Retrieves an existing Workspace in the system.
|
|
@@ -17,13 +17,66 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { ContactType } from "@fonoster/types";
|
|
20
|
-
import {
|
|
20
|
+
import { GetCapabilitiesResponse as GetCapabilitiesResponsePB } from "../generated/node/capabilities_pb";
|
|
21
|
+
import { ApplicationsClient, CallsClient, CapabilitiesClient, FonosterClient, IdentityClient, SecretsClient } from "./types";
|
|
21
22
|
import { AclsClient } from "./types/AclsClient";
|
|
22
23
|
import { AgentsClient } from "./types/AgentsClient";
|
|
23
24
|
import { CredentialsClient } from "./types/CredentialsClient";
|
|
24
25
|
import { DomainsClient } from "./types/DomainsClient";
|
|
25
26
|
import { NumbersClient } from "./types/NumbersClient";
|
|
27
|
+
import { ProvidersClient } from "./types/ProvidersClient";
|
|
26
28
|
import { TrunksClient } from "./types/TrunksClient";
|
|
29
|
+
import { WebhooksClient } from "./types/WebhooksClient";
|
|
30
|
+
type AuthMethod = "password" | "email_code" | "github";
|
|
31
|
+
/** The deployment's capabilities, from the Capabilities service. */
|
|
32
|
+
type Capabilities = {
|
|
33
|
+
auth: {
|
|
34
|
+
methods: AuthMethod[];
|
|
35
|
+
signup: "invite" | "open";
|
|
36
|
+
github?: {
|
|
37
|
+
clientId: string;
|
|
38
|
+
authorizeUrl: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
billing: {
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
};
|
|
44
|
+
costs: {
|
|
45
|
+
display: "none" | "usage" | "prices";
|
|
46
|
+
};
|
|
47
|
+
domain: string;
|
|
48
|
+
appUrl: string;
|
|
49
|
+
/**
|
|
50
|
+
* `private` is true only when the operator configured the region hub, so
|
|
51
|
+
* workspaces can enroll private regions. The local public region always
|
|
52
|
+
* exists and is not a reason to show region features.
|
|
53
|
+
*/
|
|
54
|
+
regions: {
|
|
55
|
+
private: boolean;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Where carriers and softphones reach this deployment (its default public
|
|
59
|
+
* region): SIP endpoints, addresses to allowlist and the RTP port range.
|
|
60
|
+
* Empty lists when the operator listed none or the server is older.
|
|
61
|
+
*/
|
|
62
|
+
connection: ConnectionDetails;
|
|
63
|
+
};
|
|
64
|
+
type SipTransport = "udp" | "tcp" | "tls" | "ws" | "wss";
|
|
65
|
+
type ConnectionDetails = {
|
|
66
|
+
sip: {
|
|
67
|
+
host: string;
|
|
68
|
+
port: number;
|
|
69
|
+
transport: SipTransport;
|
|
70
|
+
}[];
|
|
71
|
+
signalingAddresses: string[];
|
|
72
|
+
mediaAddresses: string[];
|
|
73
|
+
rtpPorts?: {
|
|
74
|
+
min: number;
|
|
75
|
+
max: number;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
/** The Capabilities response's connection details as plain values. */
|
|
79
|
+
declare function toConnectionDetails(response: GetCapabilitiesResponsePB): ConnectionDetails;
|
|
27
80
|
declare abstract class AbstractClient implements FonosterClient {
|
|
28
81
|
protected accessKeyId: string;
|
|
29
82
|
protected _accessToken: string;
|
|
@@ -35,10 +88,33 @@ declare abstract class AbstractClient implements FonosterClient {
|
|
|
35
88
|
identityClient: IdentityClient;
|
|
36
89
|
});
|
|
37
90
|
login(username: string, password: string, twoFactorCode?: string): Promise<void>;
|
|
38
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Signs out: forgets the tokens (right away, before the call returns) and
|
|
93
|
+
* revokes the refresh token on the server, so it can no longer be exchanged.
|
|
94
|
+
* Revocation is best effort: an unreachable server or an already-refused
|
|
95
|
+
* token does not make sign-out fail. Access tokens already issued stay valid
|
|
96
|
+
* until they expire.
|
|
97
|
+
*/
|
|
98
|
+
logout(): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Revokes `refreshToken` and every token rotated from the same sign-in.
|
|
101
|
+
* Needs no access token: holding the refresh token is the authorization.
|
|
102
|
+
*/
|
|
103
|
+
revokeToken(refreshToken: string): Promise<void>;
|
|
39
104
|
loginWithRefreshToken(refreshToken: string): Promise<void>;
|
|
40
105
|
loginWithApiKey(accessKeyId: string, accessKeySecret: string): Promise<void>;
|
|
41
106
|
loginWithOauth2Code(provider: "GITHUB", code: string): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Passwordless sign-in: trades the 6-digit code emailed by
|
|
109
|
+
* `sendVerificationCode(ContactType.EMAIL, email)` for tokens.
|
|
110
|
+
*/
|
|
111
|
+
loginWithVerificationCode(email: string, code: string): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* What the deployment supports (from the server's fonoster.yaml): sign-in
|
|
114
|
+
* methods, sign-up policy, the GitHub client id when enabled, billing, cost
|
|
115
|
+
* display, domain and app URL. Needs no credentials.
|
|
116
|
+
*/
|
|
117
|
+
getCapabilities(): Promise<Capabilities>;
|
|
42
118
|
setAccessToken(accessToken: string): void;
|
|
43
119
|
sendVerificationCode(contactType: ContactType, value: string): Promise<void>;
|
|
44
120
|
verifyCode(request: {
|
|
@@ -56,6 +132,7 @@ declare abstract class AbstractClient implements FonosterClient {
|
|
|
56
132
|
abstract getMetadata(): unknown;
|
|
57
133
|
abstract getApplicationsClient(): ApplicationsClient;
|
|
58
134
|
abstract getIdentityClient(): IdentityClient;
|
|
135
|
+
abstract getCapabilitiesClient(): CapabilitiesClient;
|
|
59
136
|
abstract getSecretsClient(): SecretsClient;
|
|
60
137
|
abstract getAgentsClient(): AgentsClient;
|
|
61
138
|
abstract getNumbersClient(): NumbersClient;
|
|
@@ -64,5 +141,8 @@ declare abstract class AbstractClient implements FonosterClient {
|
|
|
64
141
|
abstract getTrunksClient(): TrunksClient;
|
|
65
142
|
abstract getAclsClient(): AclsClient;
|
|
66
143
|
abstract getCallsClient(): CallsClient;
|
|
144
|
+
abstract getWebhooksClient(): WebhooksClient;
|
|
145
|
+
abstract getProvidersClient(): ProvidersClient;
|
|
146
|
+
abstract getHttpUrl(): string;
|
|
67
147
|
}
|
|
68
|
-
export { AbstractClient };
|
|
148
|
+
export { AbstractClient, AuthMethod, Capabilities, ConnectionDetails, toConnectionDetails };
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AbstractClient = void 0;
|
|
4
|
+
exports.toConnectionDetails = toConnectionDetails;
|
|
5
|
+
const empty_pb_1 = require("google-protobuf/google/protobuf/empty_pb");
|
|
4
6
|
const identity_pb_1 = require("../generated/node/identity_pb");
|
|
5
7
|
const makeRpcRequest_1 = require("./makeRpcRequest");
|
|
8
|
+
/** The Capabilities response's connection details as plain values. */
|
|
9
|
+
function toConnectionDetails(response) {
|
|
10
|
+
const c = response.getConnection();
|
|
11
|
+
const ports = c?.getRtpPorts();
|
|
12
|
+
return {
|
|
13
|
+
sip: (c?.getSipList() ?? []).map((s) => ({
|
|
14
|
+
host: s.getHost(),
|
|
15
|
+
port: s.getPort(),
|
|
16
|
+
transport: s.getTransport()
|
|
17
|
+
})),
|
|
18
|
+
signalingAddresses: c?.getSignalingAddressesList() ?? [],
|
|
19
|
+
mediaAddresses: c?.getMediaAddressesList() ?? [],
|
|
20
|
+
rtpPorts: ports ? { min: ports.getMin(), max: ports.getMax() } : undefined
|
|
21
|
+
};
|
|
22
|
+
}
|
|
6
23
|
class AbstractClient {
|
|
7
24
|
accessKeyId;
|
|
8
25
|
_accessToken;
|
|
@@ -31,10 +48,33 @@ class AbstractClient {
|
|
|
31
48
|
this._accessToken = accessToken;
|
|
32
49
|
this._idToken = idToken;
|
|
33
50
|
}
|
|
34
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Signs out: forgets the tokens (right away, before the call returns) and
|
|
53
|
+
* revokes the refresh token on the server, so it can no longer be exchanged.
|
|
54
|
+
* Revocation is best effort: an unreachable server or an already-refused
|
|
55
|
+
* token does not make sign-out fail. Access tokens already issued stay valid
|
|
56
|
+
* until they expire.
|
|
57
|
+
*/
|
|
58
|
+
async logout() {
|
|
59
|
+
const refreshToken = this._refreshToken;
|
|
35
60
|
this._refreshToken = "";
|
|
36
61
|
this._accessToken = "";
|
|
37
62
|
this._idToken = "";
|
|
63
|
+
if (!refreshToken)
|
|
64
|
+
return;
|
|
65
|
+
await this.revokeToken(refreshToken).catch(() => undefined);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Revokes `refreshToken` and every token rotated from the same sign-in.
|
|
69
|
+
* Needs no access token: holding the refresh token is the authorization.
|
|
70
|
+
*/
|
|
71
|
+
async revokeToken(refreshToken) {
|
|
72
|
+
await (0, makeRpcRequest_1.makeRpcRequest)({
|
|
73
|
+
method: this.identityClient.revokeToken.bind(this.identityClient),
|
|
74
|
+
requestPBObjectConstructor: identity_pb_1.RevokeTokenRequest,
|
|
75
|
+
metadata: {},
|
|
76
|
+
request: { token: refreshToken }
|
|
77
|
+
});
|
|
38
78
|
}
|
|
39
79
|
async loginWithRefreshToken(refreshToken) {
|
|
40
80
|
const { accessToken, refreshToken: newRefreshToken, idToken } = await (0, makeRpcRequest_1.makeRpcRequest)({
|
|
@@ -77,6 +117,62 @@ class AbstractClient {
|
|
|
77
117
|
this._accessToken = accessToken;
|
|
78
118
|
this._idToken = idToken;
|
|
79
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Passwordless sign-in: trades the 6-digit code emailed by
|
|
122
|
+
* `sendVerificationCode(ContactType.EMAIL, email)` for tokens.
|
|
123
|
+
*/
|
|
124
|
+
async loginWithVerificationCode(email, code) {
|
|
125
|
+
const { refreshToken, accessToken, idToken } = await (0, makeRpcRequest_1.makeRpcRequest)({
|
|
126
|
+
method: this.identityClient.exchangeVerificationCode.bind(this.identityClient),
|
|
127
|
+
requestPBObjectConstructor: identity_pb_1.ExchangeVerificationCodeRequest,
|
|
128
|
+
metadata: {},
|
|
129
|
+
request: { email, code }
|
|
130
|
+
});
|
|
131
|
+
this._refreshToken = refreshToken;
|
|
132
|
+
this._accessToken = accessToken;
|
|
133
|
+
this._idToken = idToken;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* What the deployment supports (from the server's fonoster.yaml): sign-in
|
|
137
|
+
* methods, sign-up policy, the GitHub client id when enabled, billing, cost
|
|
138
|
+
* display, domain and app URL. Needs no credentials.
|
|
139
|
+
*/
|
|
140
|
+
async getCapabilities() {
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
this.getCapabilitiesClient().getCapabilities(new empty_pb_1.Empty(), {}, (error, response) => {
|
|
143
|
+
if (error) {
|
|
144
|
+
reject(error);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const auth = response.getAuth();
|
|
148
|
+
const github = auth?.getGithub();
|
|
149
|
+
resolve({
|
|
150
|
+
auth: {
|
|
151
|
+
methods: (auth?.getMethodsList() ?? []),
|
|
152
|
+
signup: (auth?.getSignup() ||
|
|
153
|
+
"invite"),
|
|
154
|
+
github: github
|
|
155
|
+
? {
|
|
156
|
+
clientId: github.getClientId(),
|
|
157
|
+
authorizeUrl: github.getAuthorizeUrl()
|
|
158
|
+
}
|
|
159
|
+
: undefined
|
|
160
|
+
},
|
|
161
|
+
billing: { enabled: response.getBilling()?.getEnabled() ?? false },
|
|
162
|
+
costs: {
|
|
163
|
+
display: (response.getCosts()?.getDisplay() ||
|
|
164
|
+
"none")
|
|
165
|
+
},
|
|
166
|
+
domain: response.getDomain(),
|
|
167
|
+
appUrl: response.getAppUrl(),
|
|
168
|
+
regions: {
|
|
169
|
+
private: response.getRegions()?.getPrivate() ?? false
|
|
170
|
+
},
|
|
171
|
+
connection: toConnectionDetails(response)
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
80
176
|
setAccessToken(accessToken) {
|
|
81
177
|
this._accessToken = accessToken;
|
|
82
178
|
}
|
|
@@ -20,6 +20,7 @@ import { Metadata } from "@grpc/grpc-js";
|
|
|
20
20
|
import { AbstractClient } from "./AbstractClient";
|
|
21
21
|
export declare class Client extends AbstractClient {
|
|
22
22
|
private readonly endpoint;
|
|
23
|
+
private readonly httpUrl?;
|
|
23
24
|
private readonly tokenRefresherInterceptor;
|
|
24
25
|
private readonly channelCredentials;
|
|
25
26
|
constructor(config?: {
|
|
@@ -27,16 +28,22 @@ export declare class Client extends AbstractClient {
|
|
|
27
28
|
accessKeyId: string;
|
|
28
29
|
allowInsecure?: boolean;
|
|
29
30
|
withoutInterceptors?: boolean;
|
|
31
|
+
/** The HTTP API (call records); default https://<endpoint host>. */
|
|
32
|
+
httpUrl?: string;
|
|
30
33
|
});
|
|
31
34
|
getMetadata(): Metadata;
|
|
32
35
|
getApplicationsClient(): any;
|
|
33
36
|
getCallsClient(): any;
|
|
34
37
|
getIdentityClient(): any;
|
|
38
|
+
getCapabilitiesClient(): any;
|
|
35
39
|
getSecretsClient(): any;
|
|
36
40
|
getAgentsClient(): any;
|
|
37
41
|
getAclsClient(): any;
|
|
38
42
|
getDomainsClient(): any;
|
|
39
43
|
getTrunksClient(): any;
|
|
40
44
|
getNumbersClient(): any;
|
|
45
|
+
getWebhooksClient(): any;
|
|
46
|
+
getProvidersClient(): any;
|
|
47
|
+
getHttpUrl(): string;
|
|
41
48
|
getCredentialsClient(): any;
|
|
42
49
|
}
|
|
@@ -24,17 +24,21 @@ const acls_grpc_pb_1 = require("../generated/node/acls_grpc_pb");
|
|
|
24
24
|
const agents_grpc_pb_1 = require("../generated/node/agents_grpc_pb");
|
|
25
25
|
const applications_grpc_pb_1 = require("../generated/node/applications_grpc_pb");
|
|
26
26
|
const calls_grpc_pb_1 = require("../generated/node/calls_grpc_pb");
|
|
27
|
+
const capabilities_grpc_pb_1 = require("../generated/node/capabilities_grpc_pb");
|
|
27
28
|
const credentials_grpc_pb_1 = require("../generated/node/credentials_grpc_pb");
|
|
28
29
|
const domains_grpc_pb_1 = require("../generated/node/domains_grpc_pb");
|
|
29
30
|
const identity_grpc_pb_1 = require("../generated/node/identity_grpc_pb");
|
|
30
31
|
const numbers_grpc_pb_1 = require("../generated/node/numbers_grpc_pb");
|
|
32
|
+
const providers_grpc_pb_1 = require("../generated/node/providers_grpc_pb");
|
|
31
33
|
const secrets_grpc_pb_1 = require("../generated/node/secrets_grpc_pb");
|
|
32
34
|
const trunks_grpc_pb_1 = require("../generated/node/trunks_grpc_pb");
|
|
35
|
+
const webhooks_grpc_pb_1 = require("../generated/node/webhooks_grpc_pb");
|
|
33
36
|
const AbstractClient_1 = require("./AbstractClient");
|
|
34
37
|
const TokenRefresherNode_1 = require("./TokenRefresherNode");
|
|
35
38
|
const DEFAULT_ENDPOINT = "api.fonoster.com";
|
|
36
39
|
class Client extends AbstractClient_1.AbstractClient {
|
|
37
40
|
endpoint;
|
|
41
|
+
httpUrl;
|
|
38
42
|
tokenRefresherInterceptor;
|
|
39
43
|
channelCredentials;
|
|
40
44
|
constructor(config = {
|
|
@@ -52,6 +56,7 @@ class Client extends AbstractClient_1.AbstractClient {
|
|
|
52
56
|
});
|
|
53
57
|
this.channelCredentials = channelCredentials;
|
|
54
58
|
this.endpoint = config?.endpoint ?? DEFAULT_ENDPOINT;
|
|
59
|
+
this.httpUrl = config?.httpUrl;
|
|
55
60
|
this.tokenRefresherInterceptor = config.withoutInterceptors
|
|
56
61
|
? null
|
|
57
62
|
: new TokenRefresherNode_1.TokenRefresherNode(this).createInterceptor().bind(this);
|
|
@@ -75,6 +80,9 @@ class Client extends AbstractClient_1.AbstractClient {
|
|
|
75
80
|
getIdentityClient() {
|
|
76
81
|
return new identity_grpc_pb_1.IdentityClient(this.endpoint, this.channelCredentials);
|
|
77
82
|
}
|
|
83
|
+
getCapabilitiesClient() {
|
|
84
|
+
return new capabilities_grpc_pb_1.CapabilitiesClient(this.endpoint, this.channelCredentials);
|
|
85
|
+
}
|
|
78
86
|
getSecretsClient() {
|
|
79
87
|
return new secrets_grpc_pb_1.SecretsClient(this.endpoint, this.channelCredentials, {
|
|
80
88
|
interceptors: [this.tokenRefresherInterceptor]
|
|
@@ -105,6 +113,19 @@ class Client extends AbstractClient_1.AbstractClient {
|
|
|
105
113
|
interceptors: [this.tokenRefresherInterceptor]
|
|
106
114
|
});
|
|
107
115
|
}
|
|
116
|
+
getWebhooksClient() {
|
|
117
|
+
return new webhooks_grpc_pb_1.WebhooksClient(this.endpoint, this.channelCredentials, {
|
|
118
|
+
interceptors: [this.tokenRefresherInterceptor]
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
getProvidersClient() {
|
|
122
|
+
return new providers_grpc_pb_1.ProvidersClient(this.endpoint, this.channelCredentials, {
|
|
123
|
+
interceptors: [this.tokenRefresherInterceptor]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
getHttpUrl() {
|
|
127
|
+
return (this.httpUrl ?? `https://${this.endpoint.replace(/:\d+$/, "")}`).replace(/\/+$/, "");
|
|
128
|
+
}
|
|
108
129
|
getCredentialsClient() {
|
|
109
130
|
return new credentials_grpc_pb_1.CredentialsServiceClient(this.endpoint, this.channelCredentials, {
|
|
110
131
|
interceptors: [this.tokenRefresherInterceptor]
|
|
@@ -10,6 +10,23 @@ function jsonToObject(params) {
|
|
|
10
10
|
if (json[key] === null || json[key] === undefined) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
+
// Repeated fields have "List" setters (setEgressPoliciesList); objects in
|
|
14
|
+
// them are built with the constructor from objectMapping.
|
|
15
|
+
const listSetterName = `${setterName}List`;
|
|
16
|
+
if (Array.isArray(json[key]) &&
|
|
17
|
+
typeof instance[listSetterName] === "function") {
|
|
18
|
+
const constructor = objectMapping?.find((tuple) => tuple[0] === key)?.[1];
|
|
19
|
+
const items = constructor
|
|
20
|
+
? json[key].map((item) => jsonToObject({
|
|
21
|
+
json: item,
|
|
22
|
+
objectConstructor: constructor,
|
|
23
|
+
enumMapping,
|
|
24
|
+
objectMapping
|
|
25
|
+
}))
|
|
26
|
+
: json[key];
|
|
27
|
+
instance[listSetterName](items);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
13
30
|
if ((0, utils_1.isMapping)(key, enumMapping)) {
|
|
14
31
|
const enumValue = (0, utils_1.getEnumValue)(key, json[key], enumMapping);
|
|
15
32
|
instance[setterName](enumValue);
|
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.objectToJson = objectToJson;
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
|
+
/**
|
|
6
|
+
* A protobuf message to flatten. google.protobuf.Struct values (they have
|
|
7
|
+
* toJavaScript) are left as they are: callers convert them with toJavaScript().
|
|
8
|
+
*/
|
|
9
|
+
function isMessage(value) {
|
|
10
|
+
return (value !== null &&
|
|
11
|
+
typeof value === "object" &&
|
|
12
|
+
typeof value.toObject === "function" &&
|
|
13
|
+
typeof value.toJavaScript !== "function");
|
|
14
|
+
}
|
|
15
|
+
/** protobuf toObject() names repeated fields "xList"; the SDK's types say "x". */
|
|
16
|
+
function withoutListSuffix(value) {
|
|
17
|
+
if (Array.isArray(value))
|
|
18
|
+
return value.map(withoutListSuffix);
|
|
19
|
+
if (value === null || typeof value !== "object")
|
|
20
|
+
return value;
|
|
21
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
22
|
+
key.endsWith("List") && Array.isArray(item) ? key.slice(0, -4) : key,
|
|
23
|
+
withoutListSuffix(item)
|
|
24
|
+
]));
|
|
25
|
+
}
|
|
26
|
+
function plainMessage(message) {
|
|
27
|
+
return withoutListSuffix(message.toObject());
|
|
28
|
+
}
|
|
5
29
|
function objectToJson(obj, enumMapping, objectMapping, repeatableObjectMapping) {
|
|
6
30
|
const json = {};
|
|
7
31
|
const includeEmptyStringProps = new Set(["nextPageToken"]);
|
|
@@ -29,6 +53,17 @@ function objectToJson(obj, enumMapping, objectMapping, repeatableObjectMapping)
|
|
|
29
53
|
const repeatableKey = propName.slice(0, -4);
|
|
30
54
|
json[repeatableKey] = value.map((item) => objectToJson(item, enumMapping, objectMapping, repeatableObjectMapping));
|
|
31
55
|
}
|
|
56
|
+
else if (isMessage(value)) {
|
|
57
|
+
// A nested message without an explicit mapping (e.g. a domain's
|
|
58
|
+
// accessControlList): return it as a plain object, not a raw message.
|
|
59
|
+
json[propName] = plainMessage(value);
|
|
60
|
+
}
|
|
61
|
+
else if (propName.endsWith("List") &&
|
|
62
|
+
Array.isArray(value) &&
|
|
63
|
+
value.length > 0 &&
|
|
64
|
+
value.every(isMessage)) {
|
|
65
|
+
json[propName.slice(0, -4)] = value.map(plainMessage);
|
|
66
|
+
}
|
|
32
67
|
else if (value !== undefined) {
|
|
33
68
|
if (["createdAt", "updatedAt", "startedAt", "endedAt"].includes(propName)) {
|
|
34
69
|
json[propName] = new Date(value * 1000);
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
|
|
20
|
-
import { Application, CreateApplicationRequest, CreateApplicationResponse, DeleteApplicationRequest, DeleteApplicationResponse, EvaluateIntelligenceEvent, EvaluateIntelligenceRequest, GetApplicationRequest, ListApplicationsRequest, ListApplicationsResponse, TestTokenResponse, UpdateApplicationRequest, UpdateApplicationResponse } from "../../generated/web/applications_pb";
|
|
20
|
+
import { Application, CreateApplicationRequest, CreateApplicationResponse, DeleteApplicationRequest, DeleteApplicationResponse, EvaluateIntelligenceEvent, EvaluateIntelligenceRequest, GetApplicationRequest, ListApplicationsRequest, ListApplicationsResponse, RotateSigningSecretRequest, RotateSigningSecretResponse, TestTokenResponse, UpdateApplicationRequest, UpdateApplicationResponse } from "../../generated/web/applications_pb";
|
|
21
21
|
import { ClientFunction } from "./common";
|
|
22
22
|
import type { ClientReadableStream } from "grpc-web";
|
|
23
23
|
type ApplicationsClient = {
|
|
@@ -28,5 +28,6 @@ type ApplicationsClient = {
|
|
|
28
28
|
deleteApplication: ClientFunction<DeleteApplicationRequest, DeleteApplicationResponse>;
|
|
29
29
|
evaluateIntelligence: (request: EvaluateIntelligenceRequest, metadata?: Record<string, string> | null) => ClientReadableStream<EvaluateIntelligenceEvent>;
|
|
30
30
|
createTestToken: ClientFunction<Empty, TestTokenResponse>;
|
|
31
|
+
rotateSigningSecret: ClientFunction<RotateSigningSecretRequest, RotateSigningSecretResponse>;
|
|
31
32
|
};
|
|
32
33
|
export { ApplicationsClient };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { GetCapabilitiesResponse } from "../../generated/web/capabilities_pb";
|
|
20
|
+
import { ClientFunction } from "./common";
|
|
21
|
+
type CapabilitiesClient = {
|
|
22
|
+
getCapabilities: ClientFunction<unknown, GetCapabilitiesResponse>;
|
|
23
|
+
};
|
|
24
|
+
export { CapabilitiesClient };
|
|
@@ -20,18 +20,22 @@ import { AclsClient } from "./AclsClient";
|
|
|
20
20
|
import { AgentsClient } from "./AgentsClient";
|
|
21
21
|
import { ApplicationsClient } from "./ApplicationsClient";
|
|
22
22
|
import { CallsClient } from "./CallsClient";
|
|
23
|
+
import { CapabilitiesClient } from "./CapabilitiesClient";
|
|
23
24
|
import { CredentialsClient } from "./CredentialsClient";
|
|
24
25
|
import { DomainsClient } from "./DomainsClient";
|
|
25
26
|
import { IdentityClient } from "./IdentityClient";
|
|
26
27
|
import { NumbersClient } from "./NumbersClient";
|
|
28
|
+
import { ProvidersClient } from "./ProvidersClient";
|
|
27
29
|
import { SecretsClient } from "./SecretsClient";
|
|
28
30
|
import { TrunksClient } from "./TrunksClient";
|
|
31
|
+
import { WebhooksClient } from "./WebhooksClient";
|
|
29
32
|
interface FonosterClient {
|
|
30
33
|
getAccessToken(): string;
|
|
31
34
|
getAccessKeyId(): string;
|
|
32
35
|
getApplicationsClient(): ApplicationsClient;
|
|
33
36
|
getCallsClient(): CallsClient;
|
|
34
37
|
getIdentityClient(): IdentityClient;
|
|
38
|
+
getCapabilitiesClient(): CapabilitiesClient;
|
|
35
39
|
getSecretsClient(): SecretsClient;
|
|
36
40
|
getAgentsClient(): AgentsClient;
|
|
37
41
|
getNumbersClient(): NumbersClient;
|
|
@@ -39,6 +43,10 @@ interface FonosterClient {
|
|
|
39
43
|
getDomainsClient(): DomainsClient;
|
|
40
44
|
getTrunksClient(): TrunksClient;
|
|
41
45
|
getAclsClient(): AclsClient;
|
|
46
|
+
getWebhooksClient(): WebhooksClient;
|
|
47
|
+
getProvidersClient(): ProvidersClient;
|
|
48
|
+
/** Base URL of the HTTP API (/api/records), e.g. https://api.fonoster.com. */
|
|
49
|
+
getHttpUrl?(): string;
|
|
42
50
|
getMetadata(): unknown;
|
|
43
51
|
refreshToken(): Promise<void>;
|
|
44
52
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
import { CreateApiKeyRequest, CreateApiKeyResponse, CreateUserRequest, CreateUserResponse, CreateUserWithOauth2CodeRequest, CreateWorkspaceRequest, DeleteApiKeyRequest, DeleteApiKeyResponse, DeleteUserRequest, DeleteUserResponse, DeleteWorkspaceRequest, DeleteWorkspaceResponse, ExchangeApiKeyRequest, ExchangeApiKeyResponse, ExchangeCredentialsRequest, ExchangeCredentialsResponse, ExchangeOauth2CodeRequest, ExchangeOauth2CodeResponse, ExchangeRefreshTokenRequest, ExchangeRefreshTokenResponse, GetUserRequest, GetWorkspaceRequest, InviteUserToWorkspaceRequest, InviteUserToWorkspaceResponse, ListApiKeysRequest, ListApiKeysResponse, ListWorkspaceMembersRequest, ListWorkspaceMembersResponse, ListWorkspacesRequest, ListWorkspacesResponse, RegenerateApiKeyRequest, RegenerateApiKeyResponse, RemoveUserFromWorkspaceRequest, RemoveUserFromWorkspaceResponse, ResendWorkspaceMembershipInvitationRequest, ResendWorkspaceMembershipInvitationResponse, ResetPasswordRequest, SendResetPasswordCodeRequest, SendVerificationCodeRequest, UpdateUserRequest, UpdateWorkspaceRequest, UpdateWorkspaceResponse, User, VerifyCodeRequest, Workspace } from "../../generated/web/identity_pb";
|
|
19
|
+
import { CreateApiKeyRequest, CreateApiKeyResponse, CreateUserRequest, CreateUserResponse, CreateUserWithOauth2CodeRequest, CreateWorkspaceRequest, DeleteApiKeyRequest, DeleteApiKeyResponse, DeleteUserRequest, DeleteUserResponse, DeleteWorkspaceRequest, DeleteWorkspaceResponse, ExchangeApiKeyRequest, ExchangeApiKeyResponse, ExchangeCredentialsRequest, ExchangeCredentialsResponse, ExchangeOauth2CodeRequest, ExchangeOauth2CodeResponse, ExchangeRefreshTokenRequest, ExchangeRefreshTokenResponse, ExchangeVerificationCodeRequest, ExchangeVerificationCodeResponse, GetUserRequest, GetWorkspaceRequest, InviteUserToWorkspaceRequest, InviteUserToWorkspaceResponse, ListApiKeysRequest, ListApiKeysResponse, ListWorkspaceMembersRequest, ListWorkspaceMembersResponse, ListWorkspacesRequest, ListWorkspacesResponse, RegenerateApiKeyRequest, RegenerateApiKeyResponse, RemoveUserFromWorkspaceRequest, RemoveUserFromWorkspaceResponse, ResendWorkspaceMembershipInvitationRequest, ResendWorkspaceMembershipInvitationResponse, ResetPasswordRequest, RevokeTokenRequest, RevokeTokenResponse, SendResetPasswordCodeRequest, SendVerificationCodeRequest, UpdateUserRequest, UpdateWorkspaceRequest, UpdateWorkspaceResponse, User, VerifyCodeRequest, Workspace } from "../../generated/web/identity_pb";
|
|
20
20
|
import { ClientFunction } from "../types";
|
|
21
21
|
type IdentityClient = {
|
|
22
22
|
createApiKey: ClientFunction<CreateApiKeyRequest, CreateApiKeyResponse>;
|
|
@@ -27,6 +27,8 @@ type IdentityClient = {
|
|
|
27
27
|
exchangeCredentials: ClientFunction<ExchangeCredentialsRequest, ExchangeCredentialsResponse>;
|
|
28
28
|
exchangeOauth2Code: ClientFunction<ExchangeOauth2CodeRequest, ExchangeOauth2CodeResponse>;
|
|
29
29
|
exchangeRefreshToken: ClientFunction<ExchangeRefreshTokenRequest, ExchangeRefreshTokenResponse>;
|
|
30
|
+
exchangeVerificationCode: ClientFunction<ExchangeVerificationCodeRequest, ExchangeVerificationCodeResponse>;
|
|
31
|
+
revokeToken: ClientFunction<RevokeTokenRequest, RevokeTokenResponse>;
|
|
30
32
|
createUser: ClientFunction<CreateUserRequest, CreateUserResponse>;
|
|
31
33
|
createUserWithOauth2Code: ClientFunction<CreateUserWithOauth2CodeRequest, ExchangeCredentialsResponse>;
|
|
32
34
|
getUser: ClientFunction<GetUserRequest, User>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { DeleteProviderKeyRequest, DeleteProviderKeyResponse, ListCatalogRequest, ListCatalogResponse, ListProviderKeysRequest, ListProviderKeysResponse, ListWarningsRequest, ListWarningsResponse, ProviderKey, SetProviderKeyRequest } from "../../generated/web/providers_pb";
|
|
20
|
+
import { ClientFunction } from "./common";
|
|
21
|
+
type ProvidersClient = {
|
|
22
|
+
listCatalog: ClientFunction<ListCatalogRequest, ListCatalogResponse>;
|
|
23
|
+
setProviderKey: ClientFunction<SetProviderKeyRequest, ProviderKey>;
|
|
24
|
+
listProviderKeys: ClientFunction<ListProviderKeysRequest, ListProviderKeysResponse>;
|
|
25
|
+
deleteProviderKey: ClientFunction<DeleteProviderKeyRequest, DeleteProviderKeyResponse>;
|
|
26
|
+
listWarnings: ClientFunction<ListWarningsRequest, ListWarningsResponse>;
|
|
27
|
+
};
|
|
28
|
+
export { ProvidersClient };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { CreateWebhookRequest, CreateWebhookResponse, DeleteWebhookRequest, DeleteWebhookResponse, GetWebhookRequest, ListWebhookDeliveriesRequest, ListWebhookDeliveriesResponse, ListWebhooksRequest, ListWebhooksResponse, ResendWebhookDeliveryRequest, RotateWebhookSecretRequest, RotateWebhookSecretResponse, UpdateWebhookRequest, UpdateWebhookResponse, Webhook, WebhookDelivery } from "../../generated/web/webhooks_pb";
|
|
20
|
+
import { ClientFunction } from "./common";
|
|
21
|
+
type WebhooksClient = {
|
|
22
|
+
createWebhook: ClientFunction<CreateWebhookRequest, CreateWebhookResponse>;
|
|
23
|
+
listWebhooks: ClientFunction<ListWebhooksRequest, ListWebhooksResponse>;
|
|
24
|
+
getWebhook: ClientFunction<GetWebhookRequest, Webhook>;
|
|
25
|
+
updateWebhook: ClientFunction<UpdateWebhookRequest, UpdateWebhookResponse>;
|
|
26
|
+
deleteWebhook: ClientFunction<DeleteWebhookRequest, DeleteWebhookResponse>;
|
|
27
|
+
rotateWebhookSecret: ClientFunction<RotateWebhookSecretRequest, RotateWebhookSecretResponse>;
|
|
28
|
+
listWebhookDeliveries: ClientFunction<ListWebhookDeliveriesRequest, ListWebhookDeliveriesResponse>;
|
|
29
|
+
resendWebhookDelivery: ClientFunction<ResendWebhookDeliveryRequest, WebhookDelivery>;
|
|
30
|
+
};
|
|
31
|
+
export { WebhooksClient };
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
*/
|
|
19
19
|
export * from "./ApplicationsClient";
|
|
20
20
|
export * from "./CallsClient";
|
|
21
|
+
export * from "./CapabilitiesClient";
|
|
21
22
|
export * from "./FonosterClient";
|
|
22
23
|
export * from "./IdentityClient";
|
|
23
24
|
export * from "./SecretsClient";
|
|
24
25
|
export * from "./common";
|
|
26
|
+
export * from "./ProvidersClient";
|
|
27
|
+
export * from "./WebhooksClient";
|