@dynamic-labs/message-transport 4.0.0-alpha.0 → 4.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/package.json +7 -2
- package/src/index.d.ts +1 -1
- package/src/messageTypes/EmbeddedWalletsModuleMessages.d.ts +5 -1
- package/src/messageTypes/SecureStorageMessages.d.ts +5 -0
- package/src/messageTypes/UserInterfaceModuleMessages.d.ts +2 -0
- package/src/messageTypes/index.d.ts +1 -0
- package/src/requestChannel/requestChannel.cjs +63 -6
- package/src/requestChannel/requestChannel.d.ts +39 -6
- package/src/requestChannel/requestChannel.js +62 -7
- package/src/utils/logger.cjs +10 -0
- package/src/utils/logger.d.ts +2 -0
- package/src/utils/logger.js +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
|
|
2
|
+
## [4.0.0-alpha.2](https://github.com/dynamic-labs/DynamicAuth/compare/v4.0.0-alpha.1...v4.0.0-alpha.2) (2024-09-18)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* add iconVariant prop to DynamicBridgeWidget ([#6915](https://github.com/dynamic-labs/DynamicAuth/issues/6915)) ([8aa0f3d](https://github.com/dynamic-labs/DynamicAuth/commit/8aa0f3d8d8c41c7b5c4796106f611f208010cb6d))
|
|
8
|
+
* allow to create extra embedded wallets in react-native ([#6923](https://github.com/dynamic-labs/DynamicAuth/issues/6923)) ([ba22f7b](https://github.com/dynamic-labs/DynamicAuth/commit/ba22f7bcf41a444a4df0aff9b6aec257457e9402))
|
|
9
|
+
* **client:** add hide method for auth and userProfile ui modules ([#6928](https://github.com/dynamic-labs/DynamicAuth/issues/6928)) ([a11a4a5](https://github.com/dynamic-labs/DynamicAuth/commit/a11a4a5d6e25ce2a916ebd52f0b341020dc1a7e5))
|
|
10
|
+
|
|
11
|
+
## [4.0.0-alpha.1](https://github.com/dynamic-labs/DynamicAuth/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2024-09-17)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* blockaid website scanning for global connectivity ([#6874](https://github.com/dynamic-labs/DynamicAuth/issues/6874)) ([f8cbabd](https://github.com/dynamic-labs/DynamicAuth/commit/f8cbabd92fd4b5b096f47ff8e24c572ad7720dd8))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* deeplinking on ethereum and bitcoin sats connector ([#6917](https://github.com/dynamic-labs/DynamicAuth/issues/6917)) ([3896c3a](https://github.com/dynamic-labs/DynamicAuth/commit/3896c3a97819459da74dc5b4771796d7991c1f07))
|
|
22
|
+
* emit walletAdded event when createEmbeddedWalletAccount called ([#6922](https://github.com/dynamic-labs/DynamicAuth/issues/6922)) ([2a8bdd6](https://github.com/dynamic-labs/DynamicAuth/commit/2a8bdd68c50efd946c6f9b398dfae2b3585db34f))
|
|
23
|
+
* interface for sendBitcoin on unisat ([37a1bc2](https://github.com/dynamic-labs/DynamicAuth/commit/37a1bc216f43eef817c40e23b9161327f9d29c59))
|
|
24
|
+
* remove ballet crypto and duplicate keplr entry ([#6906](https://github.com/dynamic-labs/DynamicAuth/issues/6906)) ([14aeeea](https://github.com/dynamic-labs/DynamicAuth/commit/14aeeeaa8d906344f4aeddf9e73527df346b9ea0))
|
|
25
|
+
* solana and cosmos signers not working properly ([#6898](https://github.com/dynamic-labs/DynamicAuth/issues/6898)) ([6f07981](https://github.com/dynamic-labs/DynamicAuth/commit/6f079811c0d7e45b97d2dae72f4141268ab4fe0c))
|
|
26
|
+
|
|
2
27
|
## [4.0.0-alpha.0](https://github.com/dynamic-labs/DynamicAuth/compare/v3.0.0...v4.0.0-alpha.0) (2024-09-13)
|
|
3
28
|
|
|
4
29
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/message-transport",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/dynamic-labs/dynamic-auth.git",
|
|
@@ -26,8 +26,13 @@
|
|
|
26
26
|
"./package.json": "./package.json"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@dynamic-labs/sdk-api-core": "0.0.530",
|
|
29
30
|
"@vue/reactivity": "3.4.21",
|
|
30
|
-
"@dynamic-labs/types": "4.0.0-alpha.
|
|
31
|
+
"@dynamic-labs/types": "4.0.0-alpha.2",
|
|
31
32
|
"eventemitter3": "5.0.1"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@dynamic-labs/logger": "4.0.0-alpha.2",
|
|
36
|
+
"@dynamic-labs/utils": "4.0.0-alpha.2"
|
|
32
37
|
}
|
|
33
38
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -5,4 +5,4 @@ export { parseMessageTransportData } from './utils/parseMessageTransportData';
|
|
|
5
5
|
export { serializeErrorForTransport } from './utils/serializeErrorForTransport';
|
|
6
6
|
export { createEventEmitterForMessages, createStore, createStoreSetter, type CreateStoreProps, type MessagesForEventEmitter, } from './store';
|
|
7
7
|
export type { Store, StoreEventListeners, StoreKeys, StoreSetter, StoreStateChangeEvent, StoreStateEvents, StoreStateGetters, } from './store/types';
|
|
8
|
-
export { sdkHasLoadedEventName, type AuthModuleMessages, type AuthModuleState, type ClientManifest, type ConsoleMessages, type EmailOtpParams, type EmbeddedWalletsModuleMessages, type EmbeddedWalletsModuleState, type EthMessages, type EthRequestWithAddressParams, type EthRequestWithChainIdParams, type ExternalAuthMessages, type FetchMessages, type NetworksModuleState, type OtpMessages, type PasskeyMessages, type PlatformServiceMessages, type SdkModuleMessages, type SdkModuleState, type SignInWithExternalJwtParams, type SmsOtpParams, type SocialAuthModuleMessages, type SocialProvider, type SolanaMessages, type UserInterfaceModuleMessages, type VerifyWithExternalJwtParams, type WalletsModuleMessages, type WalletsModuleState, type WebViewVisibilityMessages, } from './messageTypes';
|
|
8
|
+
export { sdkHasLoadedEventName, type AuthModuleMessages, type AuthModuleState, type ClientManifest, type ConsoleMessages, type CreateEmbeddedWalletArgs, type EmailOtpParams, type EmbeddedWalletsModuleMessages, type EmbeddedWalletsModuleState, type EthMessages, type EthRequestWithAddressParams, type EthRequestWithChainIdParams, type ExternalAuthMessages, type FetchMessages, type NetworksModuleState, type OtpMessages, type PasskeyMessages, type PlatformServiceMessages, type SdkModuleMessages, type SdkModuleState, type SignInWithExternalJwtParams, type SmsOtpParams, type SecureStorageMessages, type SocialAuthModuleMessages, type SocialProvider, type SolanaMessages, type UserInterfaceModuleMessages, type VerifyWithExternalJwtParams, type WalletsModuleMessages, type WalletsModuleState, type WebViewVisibilityMessages, } from './messageTypes';
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { EmbeddedWalletChainEnum } from '@dynamic-labs/sdk-api-core';
|
|
1
2
|
import { BaseWallet } from '@dynamic-labs/types';
|
|
3
|
+
export type CreateEmbeddedWalletArgs = {
|
|
4
|
+
chain?: keyof typeof EmbeddedWalletChainEnum;
|
|
5
|
+
};
|
|
2
6
|
export type EmbeddedWalletsModuleState = {
|
|
3
7
|
hasWallet: boolean;
|
|
4
8
|
};
|
|
5
9
|
export type EmbeddedWalletsModuleMessages = {
|
|
6
10
|
getWallet: () => Promise<BaseWallet | null>;
|
|
7
|
-
createWallet: () => Promise<BaseWallet>;
|
|
11
|
+
createWallet: (args?: CreateEmbeddedWalletArgs) => Promise<BaseWallet>;
|
|
8
12
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type UserInterfaceModuleMessages = {
|
|
2
2
|
openUserProfile: () => void;
|
|
3
|
+
hideUserProfile: () => void;
|
|
3
4
|
openAuthFlow: () => void;
|
|
5
|
+
hideAuthFlow: () => void;
|
|
4
6
|
/** Opens the secret key export for embedded wallets */
|
|
5
7
|
revealEmbeddedWalletKey: (params: {
|
|
6
8
|
type: 'recovery-phrase' | 'private-key';
|
|
@@ -9,6 +9,7 @@ export * from './OtpMessages';
|
|
|
9
9
|
export * from './PasskeyMessages';
|
|
10
10
|
export * from './PlatformServiceMessages';
|
|
11
11
|
export * from './SdkModuleMessages';
|
|
12
|
+
export * from './SecureStorageMessages';
|
|
12
13
|
export * from './SocialAuthModuleMessages';
|
|
13
14
|
export * from './SolanaMessages';
|
|
14
15
|
export * from './UserInterfaceModuleMessages';
|
|
@@ -4,14 +4,32 @@
|
|
|
4
4
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
5
|
|
|
6
6
|
var _tslib = require('../../_virtual/_tslib.cjs');
|
|
7
|
+
var utils = require('@dynamic-labs/utils');
|
|
8
|
+
var isSerializedError = require('../utils/isSerializedError/isSerializedError.cjs');
|
|
9
|
+
var logger = require('../utils/logger.cjs');
|
|
7
10
|
var parseErrorFromTransport = require('../utils/parseErrorFromTransport/parseErrorFromTransport.cjs');
|
|
8
11
|
var serializeErrorForTransport = require('../utils/serializeErrorForTransport/serializeErrorForTransport.cjs');
|
|
9
|
-
var isSerializedError = require('../utils/isSerializedError/isSerializedError.cjs');
|
|
10
12
|
|
|
11
13
|
/** Given a request event name, returns the event name for its resolve */
|
|
12
14
|
const getResolveMessageType = (type) => `${type}__resolve`;
|
|
13
15
|
/** Given a request event name, returns the event name for its reject */
|
|
14
16
|
const getRejectMessageType = (type) => `${type}__reject`;
|
|
17
|
+
/** Given a request event name, returns the event name for its acknowledgement */
|
|
18
|
+
const getAckMessageType = (type) => `${type}__ack`;
|
|
19
|
+
/** Returns a "no handlers registered" error for a message type */
|
|
20
|
+
const createNoHandlerError = (type, cleanup) => {
|
|
21
|
+
const message = `No handlers were registered for message of type ${type}`;
|
|
22
|
+
logger.logger.error(message);
|
|
23
|
+
cleanup();
|
|
24
|
+
return new utils.RequestChannelNotHandledError(message);
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* When a request is sent, a timer will be started. If it times out before
|
|
28
|
+
* a corresponding ack message is received, we reject the request with NO_HANDLERS_REGISTERED.
|
|
29
|
+
*
|
|
30
|
+
* This controls how many ms we should wait before we time out.
|
|
31
|
+
*/
|
|
32
|
+
const TIMEOUT_DURATION = 3000;
|
|
15
33
|
/**
|
|
16
34
|
* Allows handling and submitting requests to and from a webview.
|
|
17
35
|
* Requests are messages that (can) expect some response.
|
|
@@ -37,22 +55,50 @@ const createRequestChannel = (messageTransport) => {
|
|
|
37
55
|
const idPrefix = Math.random().toString();
|
|
38
56
|
/** Always returns a different string */
|
|
39
57
|
const getUniqueId = () => `${idPrefix}-${uniqueIdCounter++}`;
|
|
58
|
+
/** Maps a request's session ID to its time out timer */
|
|
59
|
+
const timeoutMap = {};
|
|
40
60
|
return {
|
|
41
|
-
emit: (
|
|
61
|
+
emit: (requestType, ...params) => new Promise((resolve, reject) => {
|
|
42
62
|
// Generate the unique id for this message exchange session
|
|
43
63
|
// Although we won't listen for a response, it must still be unique
|
|
44
64
|
// to avoid tangling with other requests.
|
|
45
65
|
const messageSessionId = getUniqueId();
|
|
66
|
+
const ackMessageType = getAckMessageType(requestType);
|
|
67
|
+
// Before actually emitting the event, we start listening to the
|
|
68
|
+
// response events
|
|
69
|
+
const handleMessage = ({ messageSessionId: incomingSessionId, type: incomingType, }) => {
|
|
70
|
+
if (incomingSessionId !== messageSessionId ||
|
|
71
|
+
incomingType !== ackMessageType)
|
|
72
|
+
return;
|
|
73
|
+
clearTimeout(timeoutMap[messageSessionId]);
|
|
74
|
+
delete timeoutMap[messageSessionId];
|
|
75
|
+
resolve();
|
|
76
|
+
cleanup();
|
|
77
|
+
};
|
|
78
|
+
const cleanup = () => messageTransport.off(handleMessage);
|
|
79
|
+
messageTransport.on(handleMessage);
|
|
80
|
+
// Now we emit the event to set off the request
|
|
46
81
|
messageTransport.emit({
|
|
47
82
|
args: params,
|
|
48
83
|
messageSessionId,
|
|
49
|
-
type:
|
|
84
|
+
type: requestType,
|
|
50
85
|
});
|
|
51
|
-
|
|
86
|
+
// And start the time out timer
|
|
87
|
+
const timeoutTimer = setTimeout(() => {
|
|
88
|
+
reject(createNoHandlerError(requestType, cleanup));
|
|
89
|
+
}, TIMEOUT_DURATION);
|
|
90
|
+
timeoutMap[messageSessionId] = timeoutTimer;
|
|
91
|
+
}),
|
|
52
92
|
handle: (requestType, handler) => {
|
|
53
93
|
const messageHandler = (_a) => _tslib.__awaiter(void 0, [_a], void 0, function* ({ args, messageSessionId, type: incomingType, }) {
|
|
54
94
|
if (requestType !== incomingType)
|
|
55
95
|
return;
|
|
96
|
+
// Emit an Ack since the message will time out unless some handler acks it in time
|
|
97
|
+
messageTransport.emit({
|
|
98
|
+
args: [],
|
|
99
|
+
messageSessionId,
|
|
100
|
+
type: getAckMessageType(requestType),
|
|
101
|
+
});
|
|
56
102
|
const result = handler(...args);
|
|
57
103
|
// If the handler doesn't return a promise,
|
|
58
104
|
// that means we don't need to respond.
|
|
@@ -79,20 +125,26 @@ const createRequestChannel = (messageTransport) => {
|
|
|
79
125
|
messageTransport.off(messageHandler);
|
|
80
126
|
};
|
|
81
127
|
},
|
|
82
|
-
request: (
|
|
83
|
-
const requestType = requestName;
|
|
128
|
+
request: (requestType, ...params) => new Promise((resolve, reject) => {
|
|
84
129
|
// Generate the unique id for this message exchange session
|
|
85
130
|
const messageSessionId = getUniqueId();
|
|
86
131
|
const resolveMessageType = getResolveMessageType(requestType);
|
|
87
132
|
const rejectMessageType = getRejectMessageType(requestType);
|
|
133
|
+
const ackMessageType = getAckMessageType(requestType);
|
|
88
134
|
// Before actually emitting the event, we start listening to the
|
|
89
135
|
// response events
|
|
90
136
|
const handleMessage = ({ args: [result], messageSessionId: incomingSessionId, type: incomingType, }) => {
|
|
91
137
|
if (incomingSessionId !== messageSessionId)
|
|
92
138
|
return;
|
|
139
|
+
if (incomingType === ackMessageType) {
|
|
140
|
+
clearTimeout(timeoutMap[messageSessionId]);
|
|
141
|
+
delete timeoutMap[messageSessionId];
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
93
144
|
if (incomingType === resolveMessageType) {
|
|
94
145
|
resolve(result);
|
|
95
146
|
cleanup();
|
|
147
|
+
return;
|
|
96
148
|
}
|
|
97
149
|
if (incomingType === rejectMessageType) {
|
|
98
150
|
if (isSerializedError.isSerializedError(result)) {
|
|
@@ -106,6 +158,9 @@ const createRequestChannel = (messageTransport) => {
|
|
|
106
158
|
};
|
|
107
159
|
const cleanup = () => messageTransport.off(handleMessage);
|
|
108
160
|
messageTransport.on(handleMessage);
|
|
161
|
+
// And start the time out timer
|
|
162
|
+
const timeoutTimer = setTimeout(() => reject(createNoHandlerError(requestType, cleanup)), TIMEOUT_DURATION);
|
|
163
|
+
timeoutMap[messageSessionId] = timeoutTimer;
|
|
109
164
|
// Now we emit the event to set off the request
|
|
110
165
|
messageTransport.emit({
|
|
111
166
|
args: params,
|
|
@@ -116,6 +171,8 @@ const createRequestChannel = (messageTransport) => {
|
|
|
116
171
|
};
|
|
117
172
|
};
|
|
118
173
|
|
|
174
|
+
exports.TIMEOUT_DURATION = TIMEOUT_DURATION;
|
|
119
175
|
exports.createRequestChannel = createRequestChannel;
|
|
176
|
+
exports.getAckMessageType = getAckMessageType;
|
|
120
177
|
exports.getRejectMessageType = getRejectMessageType;
|
|
121
178
|
exports.getResolveMessageType = getResolveMessageType;
|
|
@@ -7,25 +7,49 @@ export type RequestTypes = Record<string, (...params: any[]) => Promise<any> | v
|
|
|
7
7
|
* Only the request types from T that return promises
|
|
8
8
|
* i.e. those that expect a response
|
|
9
9
|
*/
|
|
10
|
-
type TypesExpectingResponse<T extends RequestTypes> = {
|
|
10
|
+
type TypesExpectingResponse<T extends RequestTypes> = Extract<{
|
|
11
11
|
[K in keyof T]: ReturnType<T[K]> extends Promise<any> ? K : never;
|
|
12
|
-
}[keyof T]
|
|
12
|
+
}[keyof T], string>;
|
|
13
13
|
export type RequestChannel<T extends RequestTypes> = {
|
|
14
14
|
/**
|
|
15
15
|
* Listens to incoming requests of this type, and calls the handler when they arrive.
|
|
16
16
|
* If the type of this request expects some response, the handler must return a promise
|
|
17
17
|
* that resolves to this response.
|
|
18
|
+
*
|
|
18
19
|
* @returns an unsubscribe function.
|
|
19
20
|
*/
|
|
20
|
-
handle: <K extends keyof T
|
|
21
|
+
handle: <K extends Extract<keyof T, string>>(requestType: K, handler: T[K]) => VoidFunction;
|
|
21
22
|
/**
|
|
22
23
|
* Triggers handlers for this request type, with the given params.
|
|
23
|
-
* Doesn't
|
|
24
|
+
* Doesn't expect a response. Resolves as soon as the message is acknowledged by
|
|
25
|
+
* any handler.
|
|
26
|
+
*
|
|
27
|
+
* If no handlers emit an ack for this message, this will reject.
|
|
28
|
+
*
|
|
29
|
+
* Lifetime of an "emit" message:
|
|
30
|
+
* 1. "Emit" message is sent.
|
|
31
|
+
* 2. A handler receives the message.
|
|
32
|
+
* 3. The handler immediately emits an ack message for it, acknowledging it.
|
|
33
|
+
* 4. This method's promise resolves.
|
|
24
34
|
*/
|
|
25
|
-
emit: <K extends keyof T
|
|
35
|
+
emit: <K extends Extract<keyof T, string>>(requestName: K, ...params: Parameters<T[K]>) => Promise<void>;
|
|
26
36
|
/**
|
|
27
37
|
* Triggers handlers for this request type, with the given params.
|
|
28
|
-
*
|
|
38
|
+
* As opposed to emit, this expects a response message. A handler must still
|
|
39
|
+
* acknowledge this message, but the promise will only resolve when the handler
|
|
40
|
+
* emits back a response message. The promise resolves with whatever data was sent
|
|
41
|
+
* in the response.
|
|
42
|
+
*
|
|
43
|
+
* If no handlers emit an ack for this message, or if a handler responds
|
|
44
|
+
* with a failure, this will reject.
|
|
45
|
+
*
|
|
46
|
+
* Lifetime of a "request" message:
|
|
47
|
+
* 1. "Request" message is sent.
|
|
48
|
+
* 2. A handler receives the message.
|
|
49
|
+
* 3. The handler immediately emits an ack message for it, acknowledging it.
|
|
50
|
+
* 4. The handler performs the requested action.
|
|
51
|
+
* 5. The handler emits a response message.
|
|
52
|
+
* 6. This method's promise resolves with the response's data.
|
|
29
53
|
*/
|
|
30
54
|
request: <K extends TypesExpectingResponse<T>>(requestName: K, ...params: Parameters<T[K]>) => ReturnType<T[K]>;
|
|
31
55
|
};
|
|
@@ -33,6 +57,15 @@ export type RequestChannel<T extends RequestTypes> = {
|
|
|
33
57
|
export declare const getResolveMessageType: (type: string) => string;
|
|
34
58
|
/** Given a request event name, returns the event name for its reject */
|
|
35
59
|
export declare const getRejectMessageType: (type: string) => string;
|
|
60
|
+
/** Given a request event name, returns the event name for its acknowledgement */
|
|
61
|
+
export declare const getAckMessageType: (type: string) => string;
|
|
62
|
+
/**
|
|
63
|
+
* When a request is sent, a timer will be started. If it times out before
|
|
64
|
+
* a corresponding ack message is received, we reject the request with NO_HANDLERS_REGISTERED.
|
|
65
|
+
*
|
|
66
|
+
* This controls how many ms we should wait before we time out.
|
|
67
|
+
*/
|
|
68
|
+
export declare const TIMEOUT_DURATION = 3000;
|
|
36
69
|
/**
|
|
37
70
|
* Allows handling and submitting requests to and from a webview.
|
|
38
71
|
* Requests are messages that (can) expect some response.
|
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import { __awaiter } from '../../_virtual/_tslib.js';
|
|
3
|
+
import { RequestChannelNotHandledError } from '@dynamic-labs/utils';
|
|
4
|
+
import { isSerializedError } from '../utils/isSerializedError/isSerializedError.js';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
3
6
|
import { parseErrorFromTransport } from '../utils/parseErrorFromTransport/parseErrorFromTransport.js';
|
|
4
7
|
import { serializeErrorForTransport } from '../utils/serializeErrorForTransport/serializeErrorForTransport.js';
|
|
5
|
-
import { isSerializedError } from '../utils/isSerializedError/isSerializedError.js';
|
|
6
8
|
|
|
7
9
|
/** Given a request event name, returns the event name for its resolve */
|
|
8
10
|
const getResolveMessageType = (type) => `${type}__resolve`;
|
|
9
11
|
/** Given a request event name, returns the event name for its reject */
|
|
10
12
|
const getRejectMessageType = (type) => `${type}__reject`;
|
|
13
|
+
/** Given a request event name, returns the event name for its acknowledgement */
|
|
14
|
+
const getAckMessageType = (type) => `${type}__ack`;
|
|
15
|
+
/** Returns a "no handlers registered" error for a message type */
|
|
16
|
+
const createNoHandlerError = (type, cleanup) => {
|
|
17
|
+
const message = `No handlers were registered for message of type ${type}`;
|
|
18
|
+
logger.error(message);
|
|
19
|
+
cleanup();
|
|
20
|
+
return new RequestChannelNotHandledError(message);
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* When a request is sent, a timer will be started. If it times out before
|
|
24
|
+
* a corresponding ack message is received, we reject the request with NO_HANDLERS_REGISTERED.
|
|
25
|
+
*
|
|
26
|
+
* This controls how many ms we should wait before we time out.
|
|
27
|
+
*/
|
|
28
|
+
const TIMEOUT_DURATION = 3000;
|
|
11
29
|
/**
|
|
12
30
|
* Allows handling and submitting requests to and from a webview.
|
|
13
31
|
* Requests are messages that (can) expect some response.
|
|
@@ -33,22 +51,50 @@ const createRequestChannel = (messageTransport) => {
|
|
|
33
51
|
const idPrefix = Math.random().toString();
|
|
34
52
|
/** Always returns a different string */
|
|
35
53
|
const getUniqueId = () => `${idPrefix}-${uniqueIdCounter++}`;
|
|
54
|
+
/** Maps a request's session ID to its time out timer */
|
|
55
|
+
const timeoutMap = {};
|
|
36
56
|
return {
|
|
37
|
-
emit: (
|
|
57
|
+
emit: (requestType, ...params) => new Promise((resolve, reject) => {
|
|
38
58
|
// Generate the unique id for this message exchange session
|
|
39
59
|
// Although we won't listen for a response, it must still be unique
|
|
40
60
|
// to avoid tangling with other requests.
|
|
41
61
|
const messageSessionId = getUniqueId();
|
|
62
|
+
const ackMessageType = getAckMessageType(requestType);
|
|
63
|
+
// Before actually emitting the event, we start listening to the
|
|
64
|
+
// response events
|
|
65
|
+
const handleMessage = ({ messageSessionId: incomingSessionId, type: incomingType, }) => {
|
|
66
|
+
if (incomingSessionId !== messageSessionId ||
|
|
67
|
+
incomingType !== ackMessageType)
|
|
68
|
+
return;
|
|
69
|
+
clearTimeout(timeoutMap[messageSessionId]);
|
|
70
|
+
delete timeoutMap[messageSessionId];
|
|
71
|
+
resolve();
|
|
72
|
+
cleanup();
|
|
73
|
+
};
|
|
74
|
+
const cleanup = () => messageTransport.off(handleMessage);
|
|
75
|
+
messageTransport.on(handleMessage);
|
|
76
|
+
// Now we emit the event to set off the request
|
|
42
77
|
messageTransport.emit({
|
|
43
78
|
args: params,
|
|
44
79
|
messageSessionId,
|
|
45
|
-
type:
|
|
80
|
+
type: requestType,
|
|
46
81
|
});
|
|
47
|
-
|
|
82
|
+
// And start the time out timer
|
|
83
|
+
const timeoutTimer = setTimeout(() => {
|
|
84
|
+
reject(createNoHandlerError(requestType, cleanup));
|
|
85
|
+
}, TIMEOUT_DURATION);
|
|
86
|
+
timeoutMap[messageSessionId] = timeoutTimer;
|
|
87
|
+
}),
|
|
48
88
|
handle: (requestType, handler) => {
|
|
49
89
|
const messageHandler = (_a) => __awaiter(void 0, [_a], void 0, function* ({ args, messageSessionId, type: incomingType, }) {
|
|
50
90
|
if (requestType !== incomingType)
|
|
51
91
|
return;
|
|
92
|
+
// Emit an Ack since the message will time out unless some handler acks it in time
|
|
93
|
+
messageTransport.emit({
|
|
94
|
+
args: [],
|
|
95
|
+
messageSessionId,
|
|
96
|
+
type: getAckMessageType(requestType),
|
|
97
|
+
});
|
|
52
98
|
const result = handler(...args);
|
|
53
99
|
// If the handler doesn't return a promise,
|
|
54
100
|
// that means we don't need to respond.
|
|
@@ -75,20 +121,26 @@ const createRequestChannel = (messageTransport) => {
|
|
|
75
121
|
messageTransport.off(messageHandler);
|
|
76
122
|
};
|
|
77
123
|
},
|
|
78
|
-
request: (
|
|
79
|
-
const requestType = requestName;
|
|
124
|
+
request: (requestType, ...params) => new Promise((resolve, reject) => {
|
|
80
125
|
// Generate the unique id for this message exchange session
|
|
81
126
|
const messageSessionId = getUniqueId();
|
|
82
127
|
const resolveMessageType = getResolveMessageType(requestType);
|
|
83
128
|
const rejectMessageType = getRejectMessageType(requestType);
|
|
129
|
+
const ackMessageType = getAckMessageType(requestType);
|
|
84
130
|
// Before actually emitting the event, we start listening to the
|
|
85
131
|
// response events
|
|
86
132
|
const handleMessage = ({ args: [result], messageSessionId: incomingSessionId, type: incomingType, }) => {
|
|
87
133
|
if (incomingSessionId !== messageSessionId)
|
|
88
134
|
return;
|
|
135
|
+
if (incomingType === ackMessageType) {
|
|
136
|
+
clearTimeout(timeoutMap[messageSessionId]);
|
|
137
|
+
delete timeoutMap[messageSessionId];
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
89
140
|
if (incomingType === resolveMessageType) {
|
|
90
141
|
resolve(result);
|
|
91
142
|
cleanup();
|
|
143
|
+
return;
|
|
92
144
|
}
|
|
93
145
|
if (incomingType === rejectMessageType) {
|
|
94
146
|
if (isSerializedError(result)) {
|
|
@@ -102,6 +154,9 @@ const createRequestChannel = (messageTransport) => {
|
|
|
102
154
|
};
|
|
103
155
|
const cleanup = () => messageTransport.off(handleMessage);
|
|
104
156
|
messageTransport.on(handleMessage);
|
|
157
|
+
// And start the time out timer
|
|
158
|
+
const timeoutTimer = setTimeout(() => reject(createNoHandlerError(requestType, cleanup)), TIMEOUT_DURATION);
|
|
159
|
+
timeoutMap[messageSessionId] = timeoutTimer;
|
|
105
160
|
// Now we emit the event to set off the request
|
|
106
161
|
messageTransport.emit({
|
|
107
162
|
args: params,
|
|
@@ -112,4 +167,4 @@ const createRequestChannel = (messageTransport) => {
|
|
|
112
167
|
};
|
|
113
168
|
};
|
|
114
169
|
|
|
115
|
-
export { createRequestChannel, getRejectMessageType, getResolveMessageType };
|
|
170
|
+
export { TIMEOUT_DURATION, createRequestChannel, getAckMessageType, getRejectMessageType, getResolveMessageType };
|