@adobe/uix-core 0.6.5 → 0.7.1-nightly.20230114
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/__helpers__/jest.messagechannel.d.cts +2 -0
- package/dist/__helpers__/jest.messagechannel.d.cts.map +1 -0
- package/dist/__mocks__/mock-finalization-registry.d.ts +11 -0
- package/dist/__mocks__/mock-finalization-registry.d.ts.map +1 -0
- package/dist/__mocks__/mock-weak-ref.d.ts +7 -0
- package/dist/__mocks__/mock-weak-ref.d.ts.map +1 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/cross-realm-object.d.ts +44 -0
- package/dist/cross-realm-object.d.ts.map +1 -0
- package/dist/debuglog.d.ts +11 -0
- package/dist/debuglog.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +906 -7
- package/dist/index.js.map +1 -1
- package/dist/message-wrapper.d.ts +9 -0
- package/dist/message-wrapper.d.ts.map +1 -0
- package/dist/object-simulator.d.ts +28 -0
- package/dist/object-simulator.d.ts.map +1 -0
- package/dist/object-simulator.test.d.ts +2 -0
- package/dist/object-simulator.test.d.ts.map +1 -0
- package/dist/object-walker.d.ts +30 -0
- package/dist/object-walker.d.ts.map +1 -0
- package/dist/promises/index.d.ts +3 -0
- package/dist/promises/index.d.ts.map +1 -0
- package/dist/promises/promise-wrappers.test.d.ts +2 -0
- package/dist/promises/promise-wrappers.test.d.ts.map +1 -0
- package/dist/promises/timed.d.ts +15 -0
- package/dist/promises/timed.d.ts.map +1 -0
- package/dist/promises/wait.d.ts +7 -0
- package/dist/promises/wait.d.ts.map +1 -0
- package/dist/remote-subject.d.ts +70 -0
- package/dist/remote-subject.d.ts.map +1 -0
- package/dist/rpc/call-receiver.d.ts +4 -0
- package/dist/rpc/call-receiver.d.ts.map +1 -0
- package/dist/rpc/call-receiver.test.d.ts +2 -0
- package/dist/rpc/call-receiver.test.d.ts.map +1 -0
- package/dist/rpc/call-sender.d.ts +4 -0
- package/dist/rpc/call-sender.d.ts.map +1 -0
- package/dist/rpc/call-sender.test.d.ts +2 -0
- package/dist/rpc/call-sender.test.d.ts.map +1 -0
- package/dist/rpc/index.d.ts +3 -0
- package/dist/rpc/index.d.ts.map +1 -0
- package/dist/tickets.d.ts +34 -0
- package/dist/tickets.d.ts.map +1 -0
- package/dist/tunnel/index.d.ts +2 -0
- package/dist/tunnel/index.d.ts.map +1 -0
- package/dist/tunnel/tunnel-messenger.d.ts +25 -0
- package/dist/tunnel/tunnel-messenger.d.ts.map +1 -0
- package/dist/tunnel/tunnel-messenger.test.d.ts +2 -0
- package/dist/tunnel/tunnel-messenger.test.d.ts.map +1 -0
- package/dist/tunnel/tunnel.d.ts +62 -0
- package/dist/tunnel/tunnel.d.ts.map +1 -0
- package/dist/tunnel/tunnel.test.d.ts +2 -0
- package/dist/tunnel/tunnel.test.d.ts.map +1 -0
- package/dist/types.d.ts +1 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/value-assertions.d.ts +13 -0
- package/dist/value-assertions.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/__helpers__/jest.messagechannel.cjs +3 -0
- package/src/__mocks__/mock-finalization-registry.ts +13 -0
- package/src/__mocks__/mock-weak-ref.ts +10 -0
- package/src/constants.ts +10 -0
- package/src/cross-realm-object.ts +117 -0
- package/src/debuglog.ts +1 -1
- package/src/index.ts +4 -1
- package/src/message-wrapper.ts +35 -0
- package/src/object-simulator.test.ts +328 -0
- package/src/object-simulator.ts +145 -0
- package/src/object-walker.ts +132 -0
- package/src/promises/index.ts +2 -0
- package/src/promises/promise-wrappers.test.ts +63 -0
- package/src/promises/timed.ts +41 -0
- package/src/promises/wait.ts +10 -0
- package/src/remote-subject.ts +185 -0
- package/src/rpc/call-receiver.test.ts +90 -0
- package/src/rpc/call-receiver.ts +29 -0
- package/src/rpc/call-sender.test.ts +73 -0
- package/src/rpc/call-sender.ts +72 -0
- package/src/rpc/index.ts +2 -0
- package/src/tickets.ts +71 -0
- package/src/tunnel/index.ts +1 -0
- package/src/tunnel/tunnel-messenger.test.ts +183 -0
- package/src/tunnel/tunnel-messenger.ts +99 -0
- package/src/tunnel/tunnel.test.ts +211 -0
- package/src/tunnel/tunnel.ts +322 -0
- package/src/types.ts +3 -5
- package/src/value-assertions.ts +58 -0
- package/tsconfig.json +2 -6
- package/dist/timeout-promise.d.ts +0 -12
- package/dist/timeout-promise.d.ts.map +0 -1
- package/src/timeout-promise.ts +0 -36
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import EventEmitter from "eventemitter3";
|
|
2
|
+
import { isIframe } from "../value-assertions";
|
|
3
|
+
import { TunnelMessenger } from "./tunnel-messenger";
|
|
4
|
+
import { unwrap } from "../message-wrapper";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Child iframe will send offer messages to parent at this frequency until one
|
|
8
|
+
* is accepted or the attempt times out.
|
|
9
|
+
* TODO: make configurable if ever necessary
|
|
10
|
+
*/
|
|
11
|
+
const RETRY_MS = 100;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Child iframe may unexpectedly close or detach from DOM. It emits no event
|
|
15
|
+
* when this happens, so we must poll it and destroy the tunnel when necessary.
|
|
16
|
+
* TODO: make configurable if ever necessary
|
|
17
|
+
*/
|
|
18
|
+
const STATUSCHECK_MS = 5000;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Semi-unique IDs allow multiple parallel connections to handshake on both parent
|
|
22
|
+
* and child iframe. This generates a semi-random 8-char base 36 string.
|
|
23
|
+
*/
|
|
24
|
+
const KEY_BASE = 36;
|
|
25
|
+
const KEY_LENGTH = 8;
|
|
26
|
+
const KEY_EXP = KEY_BASE ** KEY_LENGTH;
|
|
27
|
+
const makeKey = () => Math.round(Math.random() * KEY_EXP).toString(KEY_BASE);
|
|
28
|
+
|
|
29
|
+
/** @alpha */
|
|
30
|
+
export interface TunnelConfig {
|
|
31
|
+
// #region Properties
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* To ensure secure communication, target origin must be specified, so the
|
|
35
|
+
* tunnel can't connect to an unauthorized domain. Can be '*' to disable
|
|
36
|
+
* origin checks, but this is discouraged!
|
|
37
|
+
*/
|
|
38
|
+
targetOrigin: string;
|
|
39
|
+
/**
|
|
40
|
+
* A Promise for a tunnel will reject if not connected within timeout (ms).
|
|
41
|
+
* @defaultValue 4000
|
|
42
|
+
*/
|
|
43
|
+
timeout: number;
|
|
44
|
+
/**
|
|
45
|
+
* Logger instance to use for debugging tunnel connection.
|
|
46
|
+
*/
|
|
47
|
+
logger: Console;
|
|
48
|
+
|
|
49
|
+
// #endregion Properties
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const badTimeout = "\n - timeout value must be a number of milliseconds";
|
|
53
|
+
const badTargetOrigin =
|
|
54
|
+
"\n - targetOrigin must be a valid URL origin or '*' for any origin";
|
|
55
|
+
|
|
56
|
+
function isFromOrigin(
|
|
57
|
+
event: MessageEvent,
|
|
58
|
+
source: WindowProxy,
|
|
59
|
+
targetOrigin: string
|
|
60
|
+
) {
|
|
61
|
+
try {
|
|
62
|
+
return (
|
|
63
|
+
source === event.source &&
|
|
64
|
+
(targetOrigin === "*" || targetOrigin === new URL(event.origin).origin)
|
|
65
|
+
);
|
|
66
|
+
} catch (_) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { emit: emitOn } = EventEmitter.prototype;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* An EventEmitter across two documents. It emits events on the remote document
|
|
75
|
+
* and takes subscribers from the local document.
|
|
76
|
+
* @alpha
|
|
77
|
+
*/
|
|
78
|
+
export class Tunnel extends EventEmitter {
|
|
79
|
+
// #region Properties
|
|
80
|
+
|
|
81
|
+
private _messagePort: MessagePort;
|
|
82
|
+
|
|
83
|
+
config: TunnelConfig;
|
|
84
|
+
|
|
85
|
+
// #endregion Properties
|
|
86
|
+
|
|
87
|
+
// #region Constructors
|
|
88
|
+
|
|
89
|
+
constructor(config: TunnelConfig) {
|
|
90
|
+
super();
|
|
91
|
+
this.config = config;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// #endregion Constructors
|
|
95
|
+
|
|
96
|
+
// #region Public Static Methods
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Create a Tunnel that connects to the page running in the provided iframe.
|
|
100
|
+
*
|
|
101
|
+
* @remarks
|
|
102
|
+
* Returns a Tunnel that listens for connection requests from the page in the
|
|
103
|
+
* provided iframe, which it will send periodically until timeout if that page
|
|
104
|
+
* has called {@link Tunnel.toParent}. If it receives one, the Tunnel will accept the
|
|
105
|
+
* connection and send an exclusive MessagePort to the xrobject on the other
|
|
106
|
+
* end. The tunnel may reconnect if the iframe reloads, in which case it will
|
|
107
|
+
* emit another "connected" event.
|
|
108
|
+
*
|
|
109
|
+
* @alpha
|
|
110
|
+
*/
|
|
111
|
+
static toIframe(
|
|
112
|
+
target: HTMLIFrameElement,
|
|
113
|
+
options: Partial<TunnelConfig>
|
|
114
|
+
): Tunnel {
|
|
115
|
+
if (!isIframe(target)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Provided tunnel target is not an iframe! ${Object.prototype.toString.call(
|
|
118
|
+
target
|
|
119
|
+
)}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const source = target.contentWindow;
|
|
124
|
+
const config = Tunnel._normalizeConfig(options);
|
|
125
|
+
const tunnel = new Tunnel(config);
|
|
126
|
+
const messenger = new TunnelMessenger({
|
|
127
|
+
myOrigin: window.location.origin,
|
|
128
|
+
targetOrigin: options.targetOrigin,
|
|
129
|
+
logger: options.logger || console,
|
|
130
|
+
});
|
|
131
|
+
let frameStatusCheck: number;
|
|
132
|
+
let timeout: number;
|
|
133
|
+
const offerListener = (event: MessageEvent) => {
|
|
134
|
+
if (
|
|
135
|
+
isFromOrigin(event, source, config.targetOrigin) &&
|
|
136
|
+
messenger.isHandshakeOffer(event.data)
|
|
137
|
+
) {
|
|
138
|
+
const accepted = messenger.makeAccepted(unwrap(event.data).offers);
|
|
139
|
+
const channel = new MessageChannel();
|
|
140
|
+
source.postMessage(accepted, config.targetOrigin, [channel.port1]);
|
|
141
|
+
tunnel.connect(channel.port2);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const cleanup = () => {
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
clearInterval(frameStatusCheck);
|
|
147
|
+
window.removeEventListener("message", offerListener);
|
|
148
|
+
};
|
|
149
|
+
timeout = window.setTimeout(() => {
|
|
150
|
+
tunnel.emitLocal(
|
|
151
|
+
"error",
|
|
152
|
+
new Error(
|
|
153
|
+
`Timed out awaiting initial message from iframe after ${config.timeout}ms`
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
tunnel.destroy();
|
|
157
|
+
}, config.timeout);
|
|
158
|
+
|
|
159
|
+
tunnel.on("destroyed", cleanup);
|
|
160
|
+
tunnel.on("connected", () => clearTimeout(timeout));
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check if the iframe has been unexpectedly removed from the DOM (for
|
|
164
|
+
* example, by React). Unsubscribe event listeners and destroy tunnel.
|
|
165
|
+
*/
|
|
166
|
+
frameStatusCheck = window.setInterval(() => {
|
|
167
|
+
if (!target.isConnected) {
|
|
168
|
+
tunnel.destroy();
|
|
169
|
+
}
|
|
170
|
+
}, STATUSCHECK_MS);
|
|
171
|
+
|
|
172
|
+
window.addEventListener("message", offerListener);
|
|
173
|
+
|
|
174
|
+
return tunnel;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Create a Tunnel that connects to the page running in the parent window.
|
|
179
|
+
*
|
|
180
|
+
* @remarks
|
|
181
|
+
* Returns a Tunnel that starts sending connection requests to the parent
|
|
182
|
+
* window, sending them periodically until the window responds with an accept
|
|
183
|
+
* message or the timeout passes. The parent window will accept the request if
|
|
184
|
+
* it calls {@link Tunnel.toIframe}.
|
|
185
|
+
*
|
|
186
|
+
* @alpha
|
|
187
|
+
*/
|
|
188
|
+
static toParent(source: WindowProxy, opts: Partial<TunnelConfig>): Tunnel {
|
|
189
|
+
let retrying: number;
|
|
190
|
+
let timeout: number;
|
|
191
|
+
let timedOut = false;
|
|
192
|
+
const key = makeKey();
|
|
193
|
+
const config = Tunnel._normalizeConfig(opts);
|
|
194
|
+
const tunnel = new Tunnel(config);
|
|
195
|
+
const messenger = new TunnelMessenger({
|
|
196
|
+
myOrigin: window.location.origin,
|
|
197
|
+
targetOrigin: config.targetOrigin,
|
|
198
|
+
logger: config.logger,
|
|
199
|
+
});
|
|
200
|
+
const acceptListener = (event: MessageEvent) => {
|
|
201
|
+
if (
|
|
202
|
+
!timedOut &&
|
|
203
|
+
isFromOrigin(event, source, config.targetOrigin) &&
|
|
204
|
+
messenger.isHandshakeAccepting(event.data, key)
|
|
205
|
+
) {
|
|
206
|
+
cleanup();
|
|
207
|
+
if (!event.ports || !event.ports.length) {
|
|
208
|
+
const portError = new Error(
|
|
209
|
+
"Received handshake accept message, but it did not include a MessagePort to establish tunnel"
|
|
210
|
+
);
|
|
211
|
+
tunnel.emitLocal("error", portError);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
tunnel.connect(event.ports[0]);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const cleanup = () => {
|
|
218
|
+
clearInterval(retrying);
|
|
219
|
+
clearTimeout(timeout);
|
|
220
|
+
window.removeEventListener("message", acceptListener);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
timeout = window.setTimeout(() => {
|
|
224
|
+
tunnel.emitLocal(
|
|
225
|
+
"error",
|
|
226
|
+
new Error(
|
|
227
|
+
`Timed out waiting for initial response from parent after ${config.timeout}ms`
|
|
228
|
+
)
|
|
229
|
+
);
|
|
230
|
+
tunnel.destroy();
|
|
231
|
+
}, config.timeout);
|
|
232
|
+
|
|
233
|
+
window.addEventListener("message", acceptListener);
|
|
234
|
+
tunnel.on("destroyed", cleanup);
|
|
235
|
+
tunnel.on("connected", cleanup);
|
|
236
|
+
|
|
237
|
+
const sendOffer = () =>
|
|
238
|
+
source.postMessage(messenger.makeOffered(key), config.targetOrigin);
|
|
239
|
+
retrying = window.setInterval(sendOffer, RETRY_MS);
|
|
240
|
+
sendOffer();
|
|
241
|
+
|
|
242
|
+
return tunnel;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// #endregion Public Static Methods
|
|
246
|
+
|
|
247
|
+
// #region Public Methods
|
|
248
|
+
|
|
249
|
+
connect(remote: MessagePort) {
|
|
250
|
+
if (this._messagePort) {
|
|
251
|
+
this._messagePort.removeEventListener("message", this._emitFromMessage);
|
|
252
|
+
this._messagePort.close();
|
|
253
|
+
}
|
|
254
|
+
this._messagePort = remote;
|
|
255
|
+
remote.addEventListener("message", this._emitFromMessage);
|
|
256
|
+
this.emit("connected");
|
|
257
|
+
this._messagePort.start();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
destroy(): void {
|
|
261
|
+
if (this._messagePort) {
|
|
262
|
+
this._messagePort.close();
|
|
263
|
+
this._messagePort = null;
|
|
264
|
+
}
|
|
265
|
+
this.emitLocal("destroyed");
|
|
266
|
+
this.emit("destroyed");
|
|
267
|
+
// this.removeAllListeners(); // TODO: maybe necessary for memory leaks
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
emit(type: string | symbol, payload?: unknown): boolean {
|
|
271
|
+
if (!this._messagePort) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
this._messagePort.postMessage({ type, payload });
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
emitLocal = (type: string | symbol, payload?: unknown) => {
|
|
279
|
+
return emitOn.call(this, type, payload);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// #endregion Public Methods
|
|
283
|
+
|
|
284
|
+
// #region Private Static Methods
|
|
285
|
+
|
|
286
|
+
private static _normalizeConfig(
|
|
287
|
+
options: Partial<TunnelConfig> = {}
|
|
288
|
+
): TunnelConfig {
|
|
289
|
+
let errorMessage = "";
|
|
290
|
+
const config: Partial<TunnelConfig> = {
|
|
291
|
+
timeout: 4000,
|
|
292
|
+
logger: console,
|
|
293
|
+
...options,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const timeoutMs = Number(config.timeout);
|
|
297
|
+
if (!Number.isSafeInteger(timeoutMs)) {
|
|
298
|
+
errorMessage += badTimeout;
|
|
299
|
+
}
|
|
300
|
+
if (config.targetOrigin !== "*") {
|
|
301
|
+
try {
|
|
302
|
+
new URL(config.targetOrigin);
|
|
303
|
+
} catch (e) {
|
|
304
|
+
errorMessage += badTargetOrigin;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (errorMessage) {
|
|
308
|
+
throw new Error(`Invalid tunnel configuration: ${errorMessage}`);
|
|
309
|
+
}
|
|
310
|
+
return config as TunnelConfig;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// #endregion Private Static Methods
|
|
314
|
+
|
|
315
|
+
// #region Private Methods
|
|
316
|
+
|
|
317
|
+
private _emitFromMessage = ({ data: { type, payload } }: MessageEvent) => {
|
|
318
|
+
this.emitLocal(type, payload);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// #endregion Private Methods
|
|
322
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -10,6 +10,8 @@ OF ANY KIND, either express or implied. See the License for the specific languag
|
|
|
10
10
|
governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { CrossRealmObject } from "./cross-realm-object";
|
|
14
|
+
|
|
13
15
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -172,11 +174,7 @@ export interface GuestConnection {
|
|
|
172
174
|
attachUI(
|
|
173
175
|
frame: HTMLIFrameElement,
|
|
174
176
|
privateMethods?: RemoteHostApis
|
|
175
|
-
):
|
|
176
|
-
promise: Promise<unknown>;
|
|
177
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
178
|
-
destroy: Function;
|
|
179
|
-
};
|
|
177
|
+
): Promise<unknown>;
|
|
180
178
|
load(): Promise<unknown>;
|
|
181
179
|
error?: Error;
|
|
182
180
|
hasCapabilities(capabilities: unknown): boolean;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
export type Primitive = string | number | boolean;
|
|
3
|
+
|
|
4
|
+
export function isPlainObject<T>(value: unknown): value is T & object {
|
|
5
|
+
if (!value || typeof value !== "object") {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const proto = Reflect.getPrototypeOf(value);
|
|
9
|
+
return proto === null || proto === Object.prototype;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isPrimitive(value: unknown): value is Primitive {
|
|
13
|
+
if (!value) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
const theType = typeof value;
|
|
17
|
+
return theType === "string" || theType === "number" || theType === "boolean";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isIterable<T>(value: unknown): value is T[] {
|
|
21
|
+
return Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isFunction(value: unknown): value is CallableFunction {
|
|
25
|
+
return typeof value === "function";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function hasProp(value: unknown, prop: string) {
|
|
29
|
+
return !isPrimitive(value) && Reflect.has(value as object, prop);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isTunnelSource(
|
|
33
|
+
value: unknown
|
|
34
|
+
): value is Window | ServiceWorker {
|
|
35
|
+
return (
|
|
36
|
+
value instanceof Window ||
|
|
37
|
+
value instanceof ServiceWorker ||
|
|
38
|
+
hasProp(value, "onmessage")
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isIframe(value: unknown): value is HTMLIFrameElement {
|
|
43
|
+
if (!value || isPrimitive(value)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const { nodeName } = value as HTMLIFrameElement;
|
|
47
|
+
return typeof nodeName === "string" && nodeName.toLowerCase() === "iframe";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function isObjectWithPrototype<T>(
|
|
51
|
+
value: unknown
|
|
52
|
+
): value is T & { [key: string | symbol]: unknown } {
|
|
53
|
+
if (!value || typeof value !== "object") {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const proto = Reflect.getPrototypeOf(value);
|
|
57
|
+
return proto !== Object.prototype;
|
|
58
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Add a timeout to a Promise. The returned Promise will resolve to the value of
|
|
3
|
-
* the original Promise, but if it doesn't resolve within the timeout interval,
|
|
4
|
-
* it will reject with a timeout error.
|
|
5
|
-
* @internal
|
|
6
|
-
*
|
|
7
|
-
* @param timeoutMs - Time to wait (ms) before rejecting
|
|
8
|
-
* @param promise - Original promise to set a timeout for
|
|
9
|
-
* @returns - Promise that rejects after X milliseconds have passed
|
|
10
|
-
*/
|
|
11
|
-
export declare function timeoutPromise<T>(timeoutMs: number, promise: Promise<T>): Promise<unknown>;
|
|
12
|
-
//# sourceMappingURL=timeout-promise.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timeout-promise.d.ts","sourceRoot":"","sources":["../src/timeout-promise.ts"],"names":[],"mappings":"AAYA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,oBAavE"}
|
package/src/timeout-promise.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2022 Adobe. All rights reserved.
|
|
3
|
-
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
-
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
-
|
|
7
|
-
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
-
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
-
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
-
governing permissions and limitations under the License.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Add a timeout to a Promise. The returned Promise will resolve to the value of
|
|
15
|
-
* the original Promise, but if it doesn't resolve within the timeout interval,
|
|
16
|
-
* it will reject with a timeout error.
|
|
17
|
-
* @internal
|
|
18
|
-
*
|
|
19
|
-
* @param timeoutMs - Time to wait (ms) before rejecting
|
|
20
|
-
* @param promise - Original promise to set a timeout for
|
|
21
|
-
* @returns - Promise that rejects after X milliseconds have passed
|
|
22
|
-
*/
|
|
23
|
-
export function timeoutPromise<T>(timeoutMs: number, promise: Promise<T>) {
|
|
24
|
-
return new Promise((resolve, reject) => {
|
|
25
|
-
const timeout = setTimeout(
|
|
26
|
-
() => reject(new Error(`Timed out after ${timeoutMs}ms`)),
|
|
27
|
-
timeoutMs
|
|
28
|
-
);
|
|
29
|
-
promise
|
|
30
|
-
.then((result) => {
|
|
31
|
-
clearTimeout(timeout);
|
|
32
|
-
resolve(result);
|
|
33
|
-
})
|
|
34
|
-
.catch(reject);
|
|
35
|
-
});
|
|
36
|
-
}
|