@parity/product-sdk-host 0.0.0-dev.312.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/LICENSE +201 -0
- package/dist/chain-discovery-nLrPzb3d.d.ts +70 -0
- package/dist/chunk-GDXSV7JV.js +50 -0
- package/dist/chunk-GDXSV7JV.js.map +1 -0
- package/dist/index.d.ts +1210 -0
- package/dist/index.js +1163 -0
- package/dist/index.js.map +1 -0
- package/dist/testing.d.ts +105 -0
- package/dist/testing.js +177 -0
- package/dist/testing.js.map +1 -0
- package/package.json +46 -0
- package/src/accounts.ts +1052 -0
- package/src/chain-discovery.ts +287 -0
- package/src/chain-spec.ts +272 -0
- package/src/chain-transaction.ts +241 -0
- package/src/chains.ts +46 -0
- package/src/chat.ts +122 -0
- package/src/container.ts +339 -0
- package/src/entropy.ts +105 -0
- package/src/errors.ts +239 -0
- package/src/features.ts +172 -0
- package/src/index.ts +149 -0
- package/src/navigation.ts +128 -0
- package/src/notifications.ts +113 -0
- package/src/papi-provider.ts +741 -0
- package/src/payments.ts +117 -0
- package/src/permissions.ts +236 -0
- package/src/result.ts +13 -0
- package/src/testing.ts +493 -0
- package/src/theme.ts +78 -0
- package/src/transport.ts +181 -0
- package/src/truapi.ts +313 -0
- package/src/types.ts +101 -0
- package/src/worker.ts +261 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Copyright 2026 Parity Technologies (UK) Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Higher-level wrappers for the host's transaction broadcast lifecycle.
|
|
5
|
+
*
|
|
6
|
+
* `truApi.chain.broadcastTransaction` / `truApi.chain.stopTransaction` are
|
|
7
|
+
* reachable via {@link getTruApi}, but consumers have to unwrap the neverthrow
|
|
8
|
+
* `ResultAsync` themselves. {@link broadcastTransaction} and
|
|
9
|
+
* {@link stopTransaction} collapse that to `Result`-returning Promises, mirroring
|
|
10
|
+
* the JSON-RPC `transaction_v1_broadcast` / `transaction_v1_stop` pair they
|
|
11
|
+
* wrap.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createLogger } from "@parity/product-sdk-logger";
|
|
17
|
+
|
|
18
|
+
import { type HostError, HostUnavailableError } from "./errors.js";
|
|
19
|
+
import { type Result, err } from "./result.js";
|
|
20
|
+
import { getTruApi, type HexString, mapHostResult } from "./truapi.js";
|
|
21
|
+
|
|
22
|
+
const log = createLogger("host:chain-transaction");
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Broadcast a signed transaction to the network via the host.
|
|
26
|
+
*
|
|
27
|
+
* Calls `truApi.chain.broadcastTransaction` and unwraps the response. The host
|
|
28
|
+
* keeps re-broadcasting until the transaction is finalized/dropped or
|
|
29
|
+
* {@link stopTransaction} is called with the returned operation id.
|
|
30
|
+
*
|
|
31
|
+
* @param genesisHash - The `0x`-prefixed genesis hash of the target chain.
|
|
32
|
+
* @param transaction - The `0x`-prefixed SCALE-encoded signed transaction.
|
|
33
|
+
* @returns `ok` with the operation id to pass to {@link stopTransaction} (or
|
|
34
|
+
* `null` if the host accepted the broadcast without issuing one), or
|
|
35
|
+
* `err(HostUnavailableError | HostCallFailedError)`.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { broadcastTransaction, stopTransaction } from "@parity/product-sdk-host";
|
|
40
|
+
*
|
|
41
|
+
* const r = await broadcastTransaction(genesisHash, signedTx);
|
|
42
|
+
* // later, to stop re-broadcasting:
|
|
43
|
+
* if (r.ok && r.value) await stopTransaction(genesisHash, r.value);
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export async function broadcastTransaction(
|
|
47
|
+
genesisHash: HexString,
|
|
48
|
+
transaction: HexString,
|
|
49
|
+
): Promise<Result<string | null, HostError>> {
|
|
50
|
+
const truApi = await getTruApi();
|
|
51
|
+
if (!truApi) {
|
|
52
|
+
return err(new HostUnavailableError("broadcastTransaction: TruAPI unavailable"));
|
|
53
|
+
}
|
|
54
|
+
log.debug("broadcastTransaction", { genesisHash });
|
|
55
|
+
|
|
56
|
+
return mapHostResult(
|
|
57
|
+
truApi.chain.broadcastTransaction({ genesisHash, transaction }),
|
|
58
|
+
(response) => response.operationId ?? null,
|
|
59
|
+
"broadcastTransaction failed",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Stop an in-flight broadcast started by {@link broadcastTransaction}.
|
|
65
|
+
*
|
|
66
|
+
* Calls `truApi.chain.stopTransaction` and unwraps the response.
|
|
67
|
+
*
|
|
68
|
+
* @param genesisHash - The `0x`-prefixed genesis hash of the target chain.
|
|
69
|
+
* @param operationId - The operation id returned by
|
|
70
|
+
* {@link broadcastTransaction}.
|
|
71
|
+
* @returns `ok` on success, or `err(HostUnavailableError | HostCallFailedError)`.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* await stopTransaction(genesisHash, operationId);
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export async function stopTransaction(
|
|
79
|
+
genesisHash: HexString,
|
|
80
|
+
operationId: string,
|
|
81
|
+
): Promise<Result<void, HostError>> {
|
|
82
|
+
const truApi = await getTruApi();
|
|
83
|
+
if (!truApi) {
|
|
84
|
+
return err(new HostUnavailableError("stopTransaction: TruAPI unavailable"));
|
|
85
|
+
}
|
|
86
|
+
log.debug("stopTransaction", { genesisHash, operationId });
|
|
87
|
+
|
|
88
|
+
return mapHostResult(
|
|
89
|
+
truApi.chain.stopTransaction({ genesisHash, operationId }),
|
|
90
|
+
() => undefined,
|
|
91
|
+
"stopTransaction failed",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (import.meta.vitest) {
|
|
96
|
+
const { test, expect, describe, vi } = import.meta.vitest;
|
|
97
|
+
|
|
98
|
+
async function withMockedTruApi<T>(
|
|
99
|
+
bridge: {
|
|
100
|
+
chain?: {
|
|
101
|
+
broadcastTransaction?: (req: unknown) => unknown;
|
|
102
|
+
stopTransaction?: (req: unknown) => unknown;
|
|
103
|
+
};
|
|
104
|
+
} | null,
|
|
105
|
+
fn: (mod: typeof import("./chain-transaction.js")) => Promise<T>,
|
|
106
|
+
): Promise<T> {
|
|
107
|
+
vi.resetModules();
|
|
108
|
+
vi.doMock("./truapi.js", async (importOriginal) => {
|
|
109
|
+
const original = await importOriginal<typeof import("./truapi.js")>();
|
|
110
|
+
return {
|
|
111
|
+
...original,
|
|
112
|
+
getTruApi: async () => bridge,
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
try {
|
|
116
|
+
const mod = await import("./chain-transaction.js");
|
|
117
|
+
return await fn(mod);
|
|
118
|
+
} finally {
|
|
119
|
+
vi.doUnmock("./truapi.js");
|
|
120
|
+
vi.resetModules();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** A resolved ResultAsync stub yielding the given response object. */
|
|
125
|
+
const ok = (response: unknown) => ({
|
|
126
|
+
match: async (onOk: (v: unknown) => unknown) => onOk(response),
|
|
127
|
+
});
|
|
128
|
+
/** A rejected ResultAsync stub yielding a truapi `GenericError` (`{ reason }`). */
|
|
129
|
+
const errResult = (reason: string) => ({
|
|
130
|
+
match: async (_onOk: (v: unknown) => unknown, onErr: (e: unknown) => unknown) =>
|
|
131
|
+
onErr({ reason }),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("broadcastTransaction", () => {
|
|
135
|
+
test("returns err(HostUnavailableError) when TruAPI is unavailable", async () => {
|
|
136
|
+
await withMockedTruApi(null, async (mod) => {
|
|
137
|
+
const result = await mod.broadcastTransaction("0x00", "0x01");
|
|
138
|
+
expect(result.ok).toBe(false);
|
|
139
|
+
if (!result.ok) {
|
|
140
|
+
expect(result.error.name).toBe("HostUnavailableError");
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("returns ok with the operation id", async () => {
|
|
146
|
+
await withMockedTruApi(
|
|
147
|
+
{
|
|
148
|
+
chain: {
|
|
149
|
+
broadcastTransaction: vi.fn().mockReturnValue(ok({ operationId: "op-1" })),
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
async (mod) => {
|
|
153
|
+
expect(await mod.broadcastTransaction("0x00", "0x01")).toEqual({
|
|
154
|
+
ok: true,
|
|
155
|
+
value: "op-1",
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("passes through a missing operation id as ok(null)", async () => {
|
|
162
|
+
await withMockedTruApi(
|
|
163
|
+
{
|
|
164
|
+
chain: {
|
|
165
|
+
broadcastTransaction: vi.fn().mockReturnValue(ok({})),
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
async (mod) => {
|
|
169
|
+
expect(await mod.broadcastTransaction("0x00", "0x01")).toEqual({
|
|
170
|
+
ok: true,
|
|
171
|
+
value: null,
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("wraps host errors in err(HostCallFailedError) with a diagnostic message", async () => {
|
|
178
|
+
await withMockedTruApi(
|
|
179
|
+
{
|
|
180
|
+
chain: {
|
|
181
|
+
broadcastTransaction: vi.fn().mockReturnValue(errResult("boom")),
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
async (mod) => {
|
|
185
|
+
const result = await mod.broadcastTransaction("0x00", "0x01");
|
|
186
|
+
expect(result.ok).toBe(false);
|
|
187
|
+
if (!result.ok) {
|
|
188
|
+
expect(result.error.name).toBe("HostCallFailedError");
|
|
189
|
+
expect(result.error.message).toMatch(/broadcastTransaction failed: boom/);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe("stopTransaction", () => {
|
|
197
|
+
test("returns err(HostUnavailableError) when TruAPI is unavailable", async () => {
|
|
198
|
+
await withMockedTruApi(null, async (mod) => {
|
|
199
|
+
const result = await mod.stopTransaction("0x00", "op-1");
|
|
200
|
+
expect(result.ok).toBe(false);
|
|
201
|
+
if (!result.ok) {
|
|
202
|
+
expect(result.error.name).toBe("HostUnavailableError");
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("returns ok on success", async () => {
|
|
208
|
+
await withMockedTruApi(
|
|
209
|
+
{
|
|
210
|
+
chain: {
|
|
211
|
+
stopTransaction: vi.fn().mockReturnValue(ok(undefined)),
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
async (mod) => {
|
|
215
|
+
expect(await mod.stopTransaction("0x00", "op-1")).toEqual({
|
|
216
|
+
ok: true,
|
|
217
|
+
value: undefined,
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("wraps host errors in err(HostCallFailedError) with a diagnostic message", async () => {
|
|
224
|
+
await withMockedTruApi(
|
|
225
|
+
{
|
|
226
|
+
chain: {
|
|
227
|
+
stopTransaction: vi.fn().mockReturnValue(errResult("boom")),
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
async (mod) => {
|
|
231
|
+
const result = await mod.stopTransaction("0x00", "op-1");
|
|
232
|
+
expect(result.ok).toBe(false);
|
|
233
|
+
if (!result.ok) {
|
|
234
|
+
expect(result.error.name).toBe("HostCallFailedError");
|
|
235
|
+
expect(result.error.message).toMatch(/stopTransaction failed: boom/);
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
}
|
package/src/chains.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Copyright 2026 Parity Technologies (UK) Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Shared chain network configuration — single source of truth for
|
|
5
|
+
* chain-specific endpoints used by multiple packages.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bulletin Chain RPC endpoints per network environment. `paseo` (Paseo Next v2)
|
|
10
|
+
* and `devnet` (public Paseo testnet) are populated today; `polkadot` and
|
|
11
|
+
* `kusama` are reserved for when those Bulletin deployments go live.
|
|
12
|
+
*/
|
|
13
|
+
export const BULLETIN_RPCS = {
|
|
14
|
+
paseo: ["wss://paseo-bulletin-next-rpc.polkadot.io"],
|
|
15
|
+
devnet: ["wss://bulletin-paseo.tservices.es:8443"],
|
|
16
|
+
polkadot: [] as string[],
|
|
17
|
+
kusama: [] as string[],
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
/** Default Bulletin Chain endpoint — the first entry under {@link BULLETIN_RPCS}.paseo. */
|
|
21
|
+
export const DEFAULT_BULLETIN_ENDPOINT: string = BULLETIN_RPCS.paseo[0];
|
|
22
|
+
|
|
23
|
+
if (import.meta.vitest) {
|
|
24
|
+
const { describe, test, expect } = import.meta.vitest;
|
|
25
|
+
|
|
26
|
+
describe("chains config", () => {
|
|
27
|
+
test("BULLETIN_RPCS has paseo endpoint", () => {
|
|
28
|
+
expect(BULLETIN_RPCS.paseo.length).toBeGreaterThan(0);
|
|
29
|
+
expect(BULLETIN_RPCS.paseo[0]).toMatch(/^wss:\/\//);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("BULLETIN_RPCS has devnet endpoint", () => {
|
|
33
|
+
expect(BULLETIN_RPCS.devnet.length).toBeGreaterThan(0);
|
|
34
|
+
expect(BULLETIN_RPCS.devnet[0]).toMatch(/^wss:\/\//);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("BULLETIN_RPCS polkadot and kusama are empty until live", () => {
|
|
38
|
+
expect(BULLETIN_RPCS.polkadot).toEqual([]);
|
|
39
|
+
expect(BULLETIN_RPCS.kusama).toEqual([]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("DEFAULT_BULLETIN_ENDPOINT matches first paseo endpoint", () => {
|
|
43
|
+
expect(DEFAULT_BULLETIN_ENDPOINT).toBe(BULLETIN_RPCS.paseo[0]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
package/src/chat.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Copyright 2026 Parity Technologies (UK) Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Wrapper for the host's chat surface, backed by `truApi.chat.*`.
|
|
5
|
+
*
|
|
6
|
+
* `getChatManager()` returns a manager for room/bot registration, message
|
|
7
|
+
* sending, and subscription to the room list and incoming actions.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
ChatBotRegistrationStatus,
|
|
14
|
+
ChatRoomRegistrationStatus,
|
|
15
|
+
HostChatActionSubscribeItem,
|
|
16
|
+
HostChatCreateRoomRequest,
|
|
17
|
+
HostChatRegisterBotRequest,
|
|
18
|
+
TrUApiClient,
|
|
19
|
+
} from "@parity/truapi";
|
|
20
|
+
|
|
21
|
+
import { getClient, subscribeWithInterrupt } from "./transport.js";
|
|
22
|
+
import { unwrapHostResult } from "./truapi.js";
|
|
23
|
+
import type { HostSubscription } from "./types.js";
|
|
24
|
+
|
|
25
|
+
/** Chat message payload variants and room metadata. Re-exported from `@parity/truapi`. */
|
|
26
|
+
export type { ChatMessageContent, ChatRoom } from "@parity/truapi";
|
|
27
|
+
import type { ChatMessageContent, ChatRoom } from "@parity/truapi";
|
|
28
|
+
|
|
29
|
+
/** Action received via {@link ChatManager.subscribeAction} (`{ roomId, peer, payload }`). Re-exported from `@parity/truapi`. */
|
|
30
|
+
export type ChatReceivedAction = HostChatActionSubscribeItem;
|
|
31
|
+
|
|
32
|
+
/** Result of registering a chat room (`"New" | "Exists"`). Re-exported from `@parity/truapi`. */
|
|
33
|
+
export type ChatRoomRegistrationResult = ChatRoomRegistrationStatus;
|
|
34
|
+
|
|
35
|
+
/** Result of registering a bot (`"New" | "Exists"`). Re-exported from `@parity/truapi`. */
|
|
36
|
+
export type ChatBotRegistrationResult = ChatBotRegistrationStatus;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Chat manager handle. Exposes room/bot registration, message sending, and
|
|
40
|
+
* subscription to the room list and incoming actions.
|
|
41
|
+
*/
|
|
42
|
+
export interface ChatManager {
|
|
43
|
+
registerRoom(request: HostChatCreateRoomRequest): Promise<ChatRoomRegistrationResult>;
|
|
44
|
+
registerBot(request: HostChatRegisterBotRequest): Promise<ChatBotRegistrationResult>;
|
|
45
|
+
sendMessage(roomId: string, payload: ChatMessageContent): Promise<{ messageId: string }>;
|
|
46
|
+
subscribeChatList(callback: (rooms: ChatRoom[]) => void): HostSubscription;
|
|
47
|
+
subscribeAction(callback: (action: ChatReceivedAction) => void): HostSubscription;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Build a {@link ChatManager} over a TruAPI client's `chat` domain. */
|
|
51
|
+
function adaptChatManager(client: TrUApiClient): ChatManager {
|
|
52
|
+
const chat = client.chat;
|
|
53
|
+
// Cache registration status by id so repeat calls don't re-prompt the host.
|
|
54
|
+
const roomStatus = new Map<string, ChatRoomRegistrationResult>();
|
|
55
|
+
const botStatus = new Map<string, ChatBotRegistrationResult>();
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
async registerRoom(request) {
|
|
59
|
+
const cached = roomStatus.get(request.roomId);
|
|
60
|
+
if (cached) return cached;
|
|
61
|
+
const response = await unwrapHostResult(
|
|
62
|
+
chat.createRoom(request),
|
|
63
|
+
"chat registerRoom failed",
|
|
64
|
+
);
|
|
65
|
+
roomStatus.set(request.roomId, response.status);
|
|
66
|
+
return response.status;
|
|
67
|
+
},
|
|
68
|
+
async registerBot(request) {
|
|
69
|
+
const cached = botStatus.get(request.botId);
|
|
70
|
+
if (cached) return cached;
|
|
71
|
+
const response = await unwrapHostResult(
|
|
72
|
+
chat.registerBot(request),
|
|
73
|
+
"chat registerBot failed",
|
|
74
|
+
);
|
|
75
|
+
botStatus.set(request.botId, response.status);
|
|
76
|
+
return response.status;
|
|
77
|
+
},
|
|
78
|
+
async sendMessage(roomId, payload) {
|
|
79
|
+
const response = await unwrapHostResult(
|
|
80
|
+
chat.postMessage({ roomId, payload }),
|
|
81
|
+
"chat sendMessage failed",
|
|
82
|
+
);
|
|
83
|
+
return { messageId: response.messageId };
|
|
84
|
+
},
|
|
85
|
+
subscribeChatList(callback) {
|
|
86
|
+
return subscribeWithInterrupt(chat.listSubscribe(), (item) => callback(item.rooms));
|
|
87
|
+
},
|
|
88
|
+
subscribeAction(callback) {
|
|
89
|
+
return subscribeWithInterrupt(chat.actionSubscribe(), callback);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get the host chat manager, backed by `truApi.chat.*`. Returns `null` when
|
|
96
|
+
* running outside a host container.
|
|
97
|
+
*
|
|
98
|
+
* @returns The chat manager, or `null` if unavailable.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* import { getChatManager } from "@parity/product-sdk-host";
|
|
103
|
+
*
|
|
104
|
+
* const chat = await getChatManager();
|
|
105
|
+
* if (chat) {
|
|
106
|
+
* await chat.registerBot({ botId: "echo", name: "Echo Bot", icon: "" });
|
|
107
|
+
* chat.subscribeAction((action) => { ... });
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export async function getChatManager(): Promise<ChatManager | null> {
|
|
112
|
+
const client = await getClient();
|
|
113
|
+
return client ? adaptChatManager(client) : null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (import.meta.vitest) {
|
|
117
|
+
const { test, expect } = import.meta.vitest;
|
|
118
|
+
|
|
119
|
+
test("getChatManager returns null outside a container", async () => {
|
|
120
|
+
expect(await getChatManager()).toBeNull();
|
|
121
|
+
});
|
|
122
|
+
}
|