summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/packages/puppeteer-core/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/packages/puppeteer-core/src/common')
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts4
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts18
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts2
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/Cookie.ts186
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts7
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts50
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts8
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts36
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts14
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts5
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts2
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/fetch.ts14
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts113
13 files changed, 296 insertions, 163 deletions
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts
index 217e53bedd..4c8308da6e 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/BrowserConnector.ts
@@ -14,7 +14,6 @@ import {isErrorLike} from '../util/ErrorLike.js';
import type {ConnectionTransport} from './ConnectionTransport.js';
import type {ConnectOptions} from './ConnectOptions.js';
import type {BrowserConnectOptions} from './ConnectOptions.js';
-import {getFetch} from './fetch.js';
const getWebSocketTransportClass = async () => {
return isNode
@@ -93,9 +92,8 @@ async function getConnectionTransport(
async function getWSEndpoint(browserURL: string): Promise<string> {
const endpointURL = new URL('/json/version', browserURL);
- const fetch = await getFetch();
try {
- const result = await fetch(endpointURL.toString(), {
+ const result = await globalThis.fetch(endpointURL.toString(), {
method: 'GET',
});
if (!result.ok) {
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts
index c64d109a7c..fe71e57587 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/Configuration.ts
@@ -32,7 +32,13 @@ export interface Configuration {
* See {@link PuppeteerNode.launch | puppeteer.launch} on how executable path
* is inferred.
*
- * @defaultValue A compatible-revision of the browser.
+ * Use a specific browser version (e.g., 119.0.6045.105). If you use an alias
+ * such `stable` or `canary` it will only work during the installation of
+ * Puppeteer and it will fail when launching the browser.
+ *
+ * @example 119.0.6045.105
+ * @defaultValue The pinned browser version supported by the current Puppeteer
+ * version.
*/
browserRevision?: string;
/**
@@ -51,20 +57,12 @@ export interface Configuration {
* @remarks
* This must include the protocol and may even need a path prefix.
*
- * @defaultValue Either https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing or
+ * @defaultValue Either https://storage.googleapis.com/chrome-for-testing-public or
* https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central,
* depending on the product.
*/
downloadBaseUrl?: string;
/**
- * Specifies the path for the downloads folder.
- *
- * Can be overridden by `PUPPETEER_DOWNLOAD_PATH`.
- *
- * @defaultValue `<cacheDirectory>`
- */
- downloadPath?: string;
- /**
* Specifies an executable path to be used in
* {@link PuppeteerNode.launch | puppeteer.launch}.
*
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts
index 85d2db9f75..c2aad7679d 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/ConsoleMessage.ts
@@ -35,7 +35,7 @@ export type ConsoleMessageType =
| 'debug'
| 'info'
| 'error'
- | 'warning'
+ | 'warn'
| 'dir'
| 'dirxml'
| 'table'
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/Cookie.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/Cookie.ts
new file mode 100644
index 0000000000..c9f7283075
--- /dev/null
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/Cookie.ts
@@ -0,0 +1,186 @@
+/**
+ * @license
+ * Copyright 2024 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * Represents the cookie's 'SameSite' status:
+ * https://tools.ietf.org/html/draft-west-first-party-cookies
+ *
+ * @public
+ */
+export type CookieSameSite = 'Strict' | 'Lax' | 'None';
+
+/**
+ * Represents the cookie's 'Priority' status:
+ * https://tools.ietf.org/html/draft-west-cookie-priority-00
+ *
+ * @public
+ */
+export type CookiePriority = 'Low' | 'Medium' | 'High';
+
+/**
+ * Represents the source scheme of the origin that originally set the cookie. A value of
+ * "Unset" allows protocol clients to emulate legacy cookie scope for the scheme.
+ * This is a temporary ability and it will be removed in the future.
+ *
+ * @public
+ */
+export type CookieSourceScheme = 'Unset' | 'NonSecure' | 'Secure';
+
+/**
+ * Represents a cookie object.
+ *
+ * @public
+ */
+export interface Cookie {
+ /**
+ * Cookie name.
+ */
+ name: string;
+ /**
+ * Cookie value.
+ */
+ value: string;
+ /**
+ * Cookie domain.
+ */
+ domain: string;
+ /**
+ * Cookie path.
+ */
+ path: string;
+ /**
+ * Cookie expiration date as the number of seconds since the UNIX epoch. Set to `-1` for
+ * session cookies
+ */
+ expires: number;
+ /**
+ * Cookie size.
+ */
+ size: number;
+ /**
+ * True if cookie is http-only.
+ */
+ httpOnly: boolean;
+ /**
+ * True if cookie is secure.
+ */
+ secure: boolean;
+ /**
+ * True in case of session cookie.
+ */
+ session: boolean;
+ /**
+ * Cookie SameSite type.
+ */
+ sameSite?: CookieSameSite;
+ /**
+ * Cookie Priority. Supported only in Chrome.
+ */
+ priority?: CookiePriority;
+ /**
+ * True if cookie is SameParty. Supported only in Chrome.
+ */
+ sameParty?: boolean;
+ /**
+ * Cookie source scheme type. Supported only in Chrome.
+ */
+ sourceScheme?: CookieSourceScheme;
+ /**
+ * Cookie partition key. The site of the top-level URL the browser was visiting at the
+ * start of the request to the endpoint that set the cookie. Supported only in Chrome.
+ */
+ partitionKey?: string;
+ /**
+ * True if cookie partition key is opaque. Supported only in Chrome.
+ */
+ partitionKeyOpaque?: boolean;
+}
+
+/**
+ * Cookie parameter object
+ *
+ * @public
+ */
+export interface CookieParam {
+ /**
+ * Cookie name.
+ */
+ name: string;
+ /**
+ * Cookie value.
+ */
+ value: string;
+ /**
+ * The request-URI to associate with the setting of the cookie. This value can affect
+ * the default domain, path, and source scheme values of the created cookie.
+ */
+ url?: string;
+ /**
+ * Cookie domain.
+ */
+ domain?: string;
+ /**
+ * Cookie path.
+ */
+ path?: string;
+ /**
+ * True if cookie is secure.
+ */
+ secure?: boolean;
+ /**
+ * True if cookie is http-only.
+ */
+ httpOnly?: boolean;
+ /**
+ * Cookie SameSite type.
+ */
+ sameSite?: CookieSameSite;
+ /**
+ * Cookie expiration date, session cookie if not set
+ */
+ expires?: number;
+ /**
+ * Cookie Priority. Supported only in Chrome.
+ */
+ priority?: CookiePriority;
+ /**
+ * True if cookie is SameParty. Supported only in Chrome.
+ */
+ sameParty?: boolean;
+ /**
+ * Cookie source scheme type. Supported only in Chrome.
+ */
+ sourceScheme?: CookieSourceScheme;
+ /**
+ * Cookie partition key. The site of the top-level URL the browser was visiting at the
+ * start of the request to the endpoint that set the cookie. If not set, the cookie will
+ * be set as not partitioned.
+ */
+ partitionKey?: string;
+}
+
+/**
+ * @public
+ */
+export interface DeleteCookiesRequest {
+ /**
+ * Name of the cookies to remove.
+ */
+ name: string;
+ /**
+ * If specified, deletes all the cookies with the given name where domain and path match
+ * provided URL. Otherwise, deletes only cookies related to the current page's domain.
+ */
+ url?: string;
+ /**
+ * If specified, deletes only cookies with the exact domain.
+ */
+ domain?: string;
+ /**
+ * If specified, deletes only cookies with the exact path.
+ */
+ path?: string;
+}
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts
index dbf5c13c95..1f1a35dd0b 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/Device.ts
@@ -1543,10 +1543,3 @@ for (const device of knownDevices) {
* @public
*/
export const KnownDevices = Object.freeze(knownDevicesByName);
-
-/**
- * @deprecated Import {@link KnownDevices}
- *
- * @public
- */
-export const devices = KnownDevices;
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts
index 8225d64f07..4d0a43ea33 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/Errors.ts
@@ -5,11 +5,11 @@
*/
/**
- * @deprecated Do not use.
+ * The base class for all Puppeteer-specific errors
*
* @public
*/
-export class CustomError extends Error {
+export class PuppeteerError extends Error {
/**
* @internal
*/
@@ -36,14 +36,14 @@ export class CustomError extends Error {
*
* @public
*/
-export class TimeoutError extends CustomError {}
+export class TimeoutError extends PuppeteerError {}
/**
* ProtocolError is emitted whenever there is an error from the protocol.
*
* @public
*/
-export class ProtocolError extends CustomError {
+export class ProtocolError extends PuppeteerError {
#code?: number;
#originalMessage = '';
@@ -76,49 +76,9 @@ export class ProtocolError extends CustomError {
*
* @public
*/
-export class UnsupportedOperation extends CustomError {}
+export class UnsupportedOperation extends PuppeteerError {}
/**
* @internal
*/
export class TargetCloseError extends ProtocolError {}
-
-/**
- * @deprecated Do not use.
- *
- * @public
- */
-export interface PuppeteerErrors {
- TimeoutError: typeof TimeoutError;
- ProtocolError: typeof ProtocolError;
-}
-
-/**
- * @deprecated Import error classes directly.
- *
- * Puppeteer methods might throw errors if they are unable to fulfill a request.
- * For example, `page.waitForSelector(selector[, options])` might fail if the
- * selector doesn't match any nodes during the given timeframe.
- *
- * For certain types of errors Puppeteer uses specific error classes. These
- * classes are available via `puppeteer.errors`.
- *
- * @example
- * An example of handling a timeout error:
- *
- * ```ts
- * try {
- * await page.waitForSelector('.foo');
- * } catch (e) {
- * if (e instanceof TimeoutError) {
- * // Do something if this is a timeout.
- * }
- * }
- * ```
- *
- * @public
- */
-export const errors: PuppeteerErrors = Object.freeze({
- TimeoutError,
- ProtocolError,
-});
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts
index cf05ef6700..f3875e99e8 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.test.ts
@@ -19,7 +19,7 @@ describe('EventEmitter', () => {
});
describe('on', () => {
- const onTests = (methodName: 'on' | 'addListener'): void => {
+ const onTests = (methodName: 'on'): void => {
it(`${methodName}: adds an event listener that is fired when the event is emitted`, () => {
const listener = sinon.spy();
emitter[methodName]('foo', listener);
@@ -43,12 +43,10 @@ describe('EventEmitter', () => {
});
};
onTests('on');
- // we support addListener for legacy reasons
- onTests('addListener');
});
describe('off', () => {
- const offTests = (methodName: 'off' | 'removeListener'): void => {
+ const offTests = (methodName: 'off'): void => {
it(`${methodName}: removes the listener so it is no longer called`, () => {
const listener = sinon.spy();
emitter.on('foo', listener);
@@ -67,8 +65,6 @@ describe('EventEmitter', () => {
});
};
offTests('off');
- // we support removeListener for legacy reasons
- offTests('removeListener');
});
describe('once', () => {
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts
index 4a8bcb801f..0aace8b2eb 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/EventEmitter.ts
@@ -27,18 +27,6 @@ export interface CommonEventEmitter<Events extends Record<EventType, unknown>> {
handler?: Handler<Events[Key]>
): this;
emit<Key extends keyof Events>(type: Key, event: Events[Key]): boolean;
- /* To maintain parity with the built in NodeJS event emitter which uses removeListener
- * rather than `off`.
- * If you're implementing new code you should use `off`.
- */
- addListener<Key extends keyof Events>(
- type: Key,
- handler: Handler<Events[Key]>
- ): this;
- removeListener<Key extends keyof Events>(
- type: Key,
- handler: Handler<Events[Key]>
- ): this;
once<Key extends keyof Events>(
type: Key,
handler: Handler<Events[Key]>
@@ -149,30 +137,6 @@ export class EventEmitter<Events extends Record<EventType, unknown>>
}
/**
- * Remove an event listener.
- *
- * @deprecated please use {@link EventEmitter.off} instead.
- */
- removeListener<Key extends keyof EventsWithWildcard<Events>>(
- type: Key,
- handler: Handler<EventsWithWildcard<Events>[Key]>
- ): this {
- return this.off(type, handler);
- }
-
- /**
- * Add an event listener.
- *
- * @deprecated please use {@link EventEmitter.on} instead.
- */
- addListener<Key extends keyof EventsWithWildcard<Events>>(
- type: Key,
- handler: Handler<EventsWithWildcard<Events>[Key]>
- ): this {
- return this.on(type, handler);
- }
-
- /**
* Like `on` but the listener will only be fired once and then it will be removed.
* @param type - the event you'd like to listen to
* @param handler - the handler function to run when the event occurs
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts
index 7cae9191a9..f87ec6817b 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/PDFOptions.ts
@@ -158,11 +158,23 @@ export interface PDFOptions {
omitBackground?: boolean;
/**
* Generate tagged (accessible) PDF.
- * @defaultValue `false`
+ * @defaultValue `true`
* @experimental
*/
tagged?: boolean;
/**
+ * Generate document outline.
+ *
+ * @remarks
+ * If this is enabled the PDF will also be tagged (accessible)
+ * Currently only works in old Headless (headless = 'shell')
+ * crbug/840455#c47
+ *
+ * @defaultValue `false`
+ * @experimental
+ */
+ outline?: boolean;
+ /**
* Timeout in milliseconds. Pass `0` to disable timeout.
* @defaultValue `30_000`
*/
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts
index 0264c9175f..d505d6c5ff 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/ScriptInjector.ts
@@ -1,3 +1,8 @@
+/**
+ * @license
+ * Copyright 2024 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
import {source as injectedSource} from '../generated/injected.js';
/**
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts
index 6ef8925605..bf4274fcf1 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/common.ts
@@ -10,12 +10,12 @@ export * from './Configuration.js';
export * from './ConnectionTransport.js';
export * from './ConnectOptions.js';
export * from './ConsoleMessage.js';
+export * from './Cookie.js';
export * from './CustomQueryHandler.js';
export * from './Debug.js';
export * from './Device.js';
export * from './Errors.js';
export * from './EventEmitter.js';
-export * from './fetch.js';
export * from './FileChooser.js';
export * from './GetQueryHandler.js';
export * from './HandleIterator.js';
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/fetch.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/fetch.ts
deleted file mode 100644
index 6c7a2b451c..0000000000
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/fetch.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * @license
- * Copyright 2020 Google Inc.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-/**
- * Gets the global version if we're in the browser, else loads the node-fetch module.
- *
- * @internal
- */
-export const getFetch = async (): Promise<typeof fetch> => {
- return (globalThis as any).fetch || (await import('cross-fetch')).fetch;
-};
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts b/remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts
index 2c8f76f664..f84453c612 100644
--- a/remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/common/util.ts
@@ -5,13 +5,19 @@
*/
import type FS from 'fs/promises';
-import type {Readable} from 'stream';
-import {map, NEVER, Observable, timer} from '../../third_party/rxjs/rxjs.js';
+import type {OperatorFunction} from '../../third_party/rxjs/rxjs.js';
+import {
+ filter,
+ from,
+ map,
+ mergeMap,
+ NEVER,
+ Observable,
+ timer,
+} from '../../third_party/rxjs/rxjs.js';
import type {CDPSession} from '../api/CDPSession.js';
-import {isNode} from '../environment.js';
import {assert} from '../util/assert.js';
-import {isErrorLike} from '../util/ErrorLike.js';
import {debug} from './Debug.js';
import {TimeoutError} from './Errors.js';
@@ -209,29 +215,39 @@ export async function importFSPromises(): Promise<typeof FS> {
* @internal
*/
export async function getReadableAsBuffer(
- readable: Readable,
+ readable: ReadableStream<Uint8Array>,
path?: string
): Promise<Buffer | null> {
- const buffers = [];
+ const buffers: Uint8Array[] = [];
+ const reader = readable.getReader();
if (path) {
const fs = await importFSPromises();
const fileHandle = await fs.open(path, 'w+');
try {
- for await (const chunk of readable) {
- buffers.push(chunk);
- await fileHandle.writeFile(chunk);
+ while (true) {
+ const {done, value} = await reader.read();
+ if (done) {
+ break;
+ }
+ buffers.push(value);
+ await fileHandle.writeFile(value);
}
} finally {
await fileHandle.close();
}
} else {
- for await (const chunk of readable) {
- buffers.push(chunk);
+ while (true) {
+ const {done, value} = await reader.read();
+ if (done) {
+ break;
+ }
+ buffers.push(value);
}
}
try {
return Buffer.concat(buffers);
} catch (error) {
+ debugError(error);
return null;
}
}
@@ -239,39 +255,34 @@ export async function getReadableAsBuffer(
/**
* @internal
*/
+
+/**
+ * @internal
+ */
export async function getReadableFromProtocolStream(
client: CDPSession,
handle: string
-): Promise<Readable> {
- // TODO: Once Node 18 becomes the lowest supported version, we can migrate to
- // ReadableStream.
- if (!isNode) {
- throw new Error('Cannot create a stream outside of Node.js environment.');
- }
+): Promise<ReadableStream<Uint8Array>> {
+ return new ReadableStream({
+ async pull(controller) {
+ function getUnit8Array(data: string, isBase64: boolean): Uint8Array {
+ if (isBase64) {
+ return Uint8Array.from(atob(data), m => {
+ return m.codePointAt(0)!;
+ });
+ }
+ const encoder = new TextEncoder();
+ return encoder.encode(data);
+ }
- const {Readable} = await import('stream');
+ const {data, base64Encoded, eof} = await client.send('IO.read', {
+ handle,
+ });
- let eof = false;
- return new Readable({
- async read(size: number) {
+ controller.enqueue(getUnit8Array(data, base64Encoded ?? false));
if (eof) {
- return;
- }
-
- try {
- const response = await client.send('IO.read', {handle, size});
- this.push(response.data, response.base64Encoded ? 'base64' : undefined);
- if (response.eof) {
- eof = true;
- await client.send('IO.close', {handle});
- this.push(null);
- }
- } catch (error) {
- if (isErrorLike(error)) {
- this.destroy(error);
- return;
- }
- throw error;
+ await client.send('IO.close', {handle});
+ controller.close();
}
},
});
@@ -349,7 +360,8 @@ export function parsePDFOptions(
pageRanges: '',
preferCSSPageSize: false,
omitBackground: false,
- tagged: false,
+ outline: false,
+ tagged: true,
};
let width = 8.5;
@@ -375,6 +387,11 @@ export function parsePDFOptions(
convertPrintParameterToInches(options.margin?.right, lengthUnit) || 0,
};
+ // Quirk https://bugs.chromium.org/p/chromium/issues/detail?id=840455#c44
+ if (options.outline) {
+ options.tagged = true;
+ }
+
return {
...defaults,
...options,
@@ -445,3 +462,21 @@ export function fromEmitterEvent<
};
});
}
+
+/**
+ * @internal
+ */
+export function filterAsync<T>(
+ predicate: (value: T) => boolean | PromiseLike<boolean>
+): OperatorFunction<T, T> {
+ return mergeMap<T, Observable<T>>((value): Observable<T> => {
+ return from(Promise.resolve(predicate(value))).pipe(
+ filter(isMatch => {
+ return isMatch;
+ }),
+ map(() => {
+ return value;
+ })
+ );
+ });
+}