export function initNativeHandlers(contentRef: ContentRef, store: *) { const state$ = from(store).pipe(share()); return createTitleFeed(contentRef, state$).subscribe( setTitleFromAttributes, err => console.error(err) ); }
/** * @param {?=} config */ constructor(config = {}) { this.isSupported = false; this.notifyOptions = { setItem: false, removeItem: false }; this.prefix = 'ls'; this.storageType = 'localStorage'; this.errors = new Subscriber(); this.removeItems = new Subscriber(); this.setItems = new Subscriber(); this.warnings = new Subscriber(); let { notifyOptions, prefix, storageType } = config; if (notifyOptions != null) { let { setItem, removeItem } = notifyOptions; this.setNotify(!!setItem, !!removeItem); } if (prefix != null) { this.setPrefix(prefix); } if (storageType != null) { this.setStorageType(storageType); } this.errors$ = new Observable((/** * @param {?} observer * @return {?} */ (observer) => this.errors = observer)).pipe(share()); this.removeItems$ = new Observable((/** * @param {?} observer * @return {?} */ (observer) => this.removeItems = observer)).pipe(share()); this.setItems$ = new Observable((/** * @param {?} observer * @return {?} */ (observer) => this.setItems = observer)).pipe(share()); this.warnings$ = new Observable((/** * @param {?} observer * @return {?} */ (observer) => this.warnings = observer)).pipe(share()); this.isSupported = this.checkSupport(); }
link: function ($scope, $elem, attrs, ngModel) { if ($elem.prop('multiple')) { throw new Error(multipleUsageErrorMessage); } const maxSizeValidator = (dataUrl) => { return { errorKey: 'maxSize', isValid: attrs.max === '' || dataUrl.length <= parseInt(attrs.max) }; }; const validators = [ maxSizeValidator ]; // produce fileContent$ whenever the $element 'change' event is triggered. const fileContent$ = Rx.fromEvent($elem, 'change', e => e).pipe( map(e => e.target.files), switchMap(files => { if (files.length === 0) { return []; } if (files.length > 1) { throw new Error(multipleUsageErrorMessage); } return createFileContent$(files[0]); }), share() ); // validate the content of the files after it is loaded const validations$ = fileContent$.pipe( map(fileContent => ( validators.map(validator => validator(fileContent)) )) ); // push results from input/validation to the ngModel const unsubscribe = Rx .combineLatest(fileContent$, validations$) .subscribe(([ fileContent, validations ]) => { $scope.$evalAsync(() => { validations.forEach(validation => { ngModel.$setValidity(validation.errorKey, validation.isValid); }); if (validations.every(validation => validation.isValid)) { ngModel.$setViewValue(fileContent); } }); }, (err) => { throw err; }); $scope.$on('destroy', unsubscribe); }
TranslateService.prototype.getTranslation = function (lang) { var _this = this; this.pending = true; this.loadingTranslations = this.currentLoader.getTranslation(lang).pipe(share()); this.loadingTranslations.pipe(take(1)) .subscribe(function (res) { _this.translations[lang] = _this.compiler.compileTranslations(res, lang); _this.updateLangs(); _this.pending = false; }, function (err) { _this.pending = false; }); return this.loadingTranslations; };
constructor(props) { super(props); const { vis } = props; this.appState = vis.API.getAppState(); this.state = { model: props.visParams, dirty: false, autoApply: true, visFields: props.visFields }; this.onBrush = brushHandler(props.vis.API.timeFilter); this.visDataSubject = new Rx.Subject(); this.visData$ = this.visDataSubject.asObservable().pipe(share()); }
bindMessageHandlers(client, handlers, transform) { const disconnect$ = rxjs_1.fromEvent(client, 'disconnect').pipe(operators_1.share(), operators_1.first()); handlers.forEach(({ message, callback }) => { const source$ = rxjs_1.fromEvent(client, message).pipe(operators_1.mergeMap((payload) => { const { data, ack } = this.mapPayload(payload); return transform(callback(data)).pipe(operators_1.filter(response => !shared_utils_1.isNil(response)), operators_1.map(response => [response, ack])); }), operators_1.takeUntil(disconnect$)); source$.subscribe(([response, ack]) => { if (response.event) { return client.emit(response.event, response.data); } shared_utils_1.isFunction(ack) && ack(response); }); }); }
test("throws an error if given a bad action", done => { const actionBuffer = []; const action$ = ActionsObservable.of({ type: actionTypes.LAUNCH_KERNEL }).pipe(share()); const obs = launchKernelEpic(action$); obs.subscribe( x => { expect(x.type).toEqual(actionTypes.LAUNCH_KERNEL_FAILED); actionBuffer.push(x.type); done(); }, err => done.fail(err) ); });
export function safeChildProcess(childProcess, observer) { const ownTerminateSignal$ = Rx.merge( Rx.fromEvent(process, 'SIGTERM').pipe(mapTo('SIGTERM')), Rx.fromEvent(process, 'SIGINT').pipe(mapTo('SIGINT')), Rx.fromEvent(process, 'SIGBREAK').pipe(mapTo('SIGBREAK')), ) .pipe( take(1), share() ); // signals that will be sent to the child process as a result of the main process // being sent these signals, or the exit being triggered const signalForChildProcess$ = Rx.merge( // SIGKILL when this process gets a terminal signal ownTerminateSignal$.pipe( mapTo('SIGKILL') ), // SIGKILL when this process forcefully exits Rx.fromEvent(process, 'exit').pipe( take(1), mapTo('SIGKILL') ), ); // send termination signals const terminate$ = Rx.merge( signalForChildProcess$.pipe( tap(signal => childProcess.kill(signal)) ), ownTerminateSignal$.pipe( delay(1), tap(signal => process.kill(process.pid, signal)), ) ); // this is adding unsubscribe logic to our observer // so that if our observer unsubscribes, we terminate our child-process observer.add(() => { childProcess.kill('SIGKILL'); }); observer.add(terminate$.pipe(ignoreElements()).subscribe(observer)); }
test("Errors on an action where source not a string", done => { const badAction$ = ActionsObservable.of( actions.executeCell({ id: "id" }) ).pipe(share()); const responseActions = executeCellEpic(badAction$, store).pipe( catchError(error => { expect(error.message).toEqual("execute cell needs source string"); }) ); responseActions.subscribe( // Every action that goes through should get stuck on an array x => { expect(x.type).toEqual(actionTypes.EXECUTE_FAILED); done(); }, err => done.fail(err) ); });
/** * @return {?} */ createActionStreams() { // Listens to all changes based on our instanceId const /** @type {?} */ changes$ = this.createChangesObservable().pipe(share()); // Listen for the start action const /** @type {?} */ start$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.START)); // Listen for the stop action const /** @type {?} */ stop$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.STOP)); // Listen for lifted actions const /** @type {?} */ liftedActions$ = changes$.pipe(filter(change => change.type === ExtensionActionTypes.DISPATCH), map(change => this.unwrapAction(change.payload))); // Listen for unlifted actions const /** @type {?} */ actions$ = changes$.pipe(filter(change => change.type === ExtensionActionTypes.ACTION), map(change => this.unwrapAction(change.payload))); const /** @type {?} */ actionsUntilStop$ = actions$.pipe(takeUntil(stop$)); const /** @type {?} */ liftedUntilStop$ = liftedActions$.pipe(takeUntil(stop$)); this.start$ = start$.pipe(takeUntil(stop$)); // Only take the action sources between the start/stop events this.actions$ = this.start$.pipe(switchMap(() => actionsUntilStop$)); this.liftedActions$ = this.start$.pipe(switchMap(() => liftedUntilStop$)); }
DevtoolsExtension.prototype.createActionStreams = function () { var _this = this; // Listens to all changes based on our instanceId var changes$ = this.createChangesObservable().pipe(share()); // Listen for the start action var start$ = changes$.pipe(filter(function (change) { return change.type === ExtensionActionTypes.START; })); // Listen for the stop action var stop$ = changes$.pipe(filter(function (change) { return change.type === ExtensionActionTypes.STOP; })); // Listen for lifted actions var liftedActions$ = changes$.pipe(filter(function (change) { return change.type === ExtensionActionTypes.DISPATCH; }), map(function (change) { return _this.unwrapAction(change.payload); })); // Listen for unlifted actions var actions$ = changes$.pipe(filter(function (change) { return change.type === ExtensionActionTypes.ACTION; }), map(function (change) { return _this.unwrapAction(change.payload); })); var actionsUntilStop$ = actions$.pipe(takeUntil(stop$)); var liftedUntilStop$ = liftedActions$.pipe(takeUntil(stop$)); this.start$ = start$.pipe(takeUntil(stop$)); // Only take the action sources between the start/stop events this.actions$ = this.start$.pipe(switchMap(function () { return actionsUntilStop$; })); this.liftedActions$ = this.start$.pipe(switchMap(function () { return liftedUntilStop$; })); };
module.exports = function(rl) { var keypress = fromEvent(rl.input, 'keypress', normalizeKeypressEvents) // Ignore `enter` key. On the readline, we only care about the `line` event. .pipe(filter(({ key }) => key.name !== 'enter' && key.name !== 'return')); return { line: fromEvent(rl, 'line'), keypress: keypress, normalizedUpKey: keypress.pipe( filter( ({ key }) => key.name === 'up' || key.name === 'k' || (key.name === 'p' && key.ctrl) ), share() ), normalizedDownKey: keypress.pipe( filter( ({ key }) => key.name === 'down' || key.name === 'j' || (key.name === 'n' && key.ctrl) ), share() ), numberKey: keypress.pipe( filter(e => e.value && '123456789'.indexOf(e.value) >= 0), map(e => Number(e.value)), share() ), spaceKey: keypress.pipe( filter(({ key }) => key && key.name === 'space'), share() ), aKey: keypress.pipe( filter(({ key }) => key && key.name === 'a'), share() ), iKey: keypress.pipe( filter(({ key }) => key && key.name === 'i'), share() ) }; };
export function fromRef(ref, event, listenType) { if (listenType === void 0) { listenType = 'on'; } return new Observable(function (subscriber) { var fn = ref[listenType](event, function (snapshot, prevKey) { subscriber.next({ snapshot: snapshot, prevKey: prevKey }); if (listenType == 'once') { subscriber.complete(); } }, subscriber.error.bind(subscriber)); if (listenType == 'on') { return { unsubscribe: function () { ref.off(event, fn); } }; } else { return { unsubscribe: function () { } }; } }).pipe(map(function (payload) { var snapshot = payload.snapshot, prevKey = payload.prevKey; var key = null; if (snapshot.exists()) { key = snapshot.key; } return { type: event, payload: snapshot, prevKey: prevKey, key: key }; }), delay(0), share()); }
export function findPluginSpecs(settings, configToMutate) { const config$ = Rx .defer(async () => { if (configToMutate) { return configToMutate; } return await defaultConfig(settings); }) .pipe(shareReplay()); // find plugin packs in configured paths/dirs const packageJson$ = config$.pipe( mergeMap(config => Rx.merge( ...config.get('plugins.paths').map(createPackageJsonAtPath$), ...config.get('plugins.scanDirs').map(createPackageJsonsInDirectory$) )), distinct(getDistinctKeyForFindResult), share() ); const pack$ = createPack$(packageJson$).pipe( share() ); const extendConfig$ = config$.pipe( mergeMap(config => ( pack$.pipe( // get the specs for each found plugin pack mergeMap(({ pack }) => ( pack ? pack.getPluginSpecs() : [] )), // make sure that none of the plugin specs have conflicting ids, fail // early if conflicts detected or merge the specs back into the stream toArray(), mergeMap(allSpecs => { for (const [id, specs] of groupSpecsById(allSpecs)) { if (specs.length > 1) { throw new Error( `Multiple plugins found with the id "${id}":\n${ specs.map(spec => ` - ${id} at ${spec.getPath()}`).join('\n') }` ); } } return allSpecs; }), mergeMap(async (spec) => { // extend the config service with this plugin spec and // collect its deprecations messages if some of its // settings are outdated const deprecations = []; await extendConfigService(spec, config, settings, (message) => { deprecations.push({ spec, message }); }); return { spec, deprecations, }; }), // extend the config with all plugins before determining enabled status bufferAllResults, map(({ spec, deprecations }) => { const isRightVersion = spec.isVersionCompatible(config.get('pkg.version')); const enabled = isRightVersion && spec.isEnabled(config); return { config, spec, deprecations, enabledSpecs: enabled ? [spec] : [], disabledSpecs: enabled ? [] : [spec], invalidVersionSpecs: isRightVersion ? [] : [spec], }; }), // determine which plugins are disabled before actually removing things from the config bufferAllResults, tap(result => { for (const spec of result.disabledSpecs) { disableConfigExtension(spec, config); } }) ) )), share() ); return { // package JSONs found when searching configure paths packageJson$: packageJson$.pipe( mergeMap(result => ( result.packageJson ? [result.packageJson] : [] )) ), // plugin packs found when searching configured paths pack$: pack$.pipe( mergeMap(result => ( result.pack ? [result.pack] : [] )) ), // errors caused by invalid directories of plugin directories invalidDirectoryError$: pack$.pipe( mergeMap(result => ( isInvalidDirectoryError(result.error) ? [result.error] : [] )) ), // errors caused by directories that we expected to be plugin but were invalid invalidPackError$: pack$.pipe( mergeMap(result => ( isInvalidPackError(result.error) ? [result.error] : [] )) ), otherError$: pack$.pipe( mergeMap(result => ( isUnhandledError(result.error) ? [result.error] : [] )) ), // { spec, message } objects produced when transforming deprecated // settings for a plugin spec deprecation$: extendConfig$.pipe( mergeMap(result => result.deprecations) ), // the config service we extended with all of the plugin specs, // only emitted once it is fully extended by all extendedConfig$: extendConfig$.pipe( mergeMap(result => result.config), filter(Boolean), last() ), // all enabled PluginSpec objects spec$: extendConfig$.pipe( mergeMap(result => result.enabledSpecs) ), // all disabled PluginSpec objects disabledSpec$: extendConfig$.pipe( mergeMap(result => result.disabledSpecs) ), // all PluginSpec objects that were disabled because their version was incompatible invalidVersionSpec$: extendConfig$.pipe( mergeMap(result => result.invalidVersionSpecs) ), }; }
/** * Returns a new Observable that multicasts (shares) the original Observable. As long as there is at least one * Subscriber this Observable will be subscribed and emitting data. When all subscribers have unsubscribed it will * unsubscribe from the source Observable. Because the Observable is multicasting it makes the stream `hot`. * * This behaves similarly to .publish().refCount(), with a behavior difference when the source observable emits complete. * .publish().refCount() will not resubscribe to the original source, however .share() will resubscribe to the original source. * Observable.of("test").publish().refCount() will not re-emit "test" on new subscriptions, Observable.of("test").share() will * re-emit "test" to new subscriptions. * * <img src="./img/share.png" width="100%"> * * @return {Observable<T>} An Observable that upon connection causes the source Observable to emit items to its Observers. * @method share * @owner Observable */ function share() { return operators_1.share()(this); }
function setupDrawer() { const size$ = createXObservable(ResizeObserver)(drawerEl).pipe( map( () => window.matchMedia(BREAK_POINT_DYNAMIC).matches ? LARGE_DESKTOP : window.matchMedia(BREAK_POINT_3).matches ? DESKTOP : MOBILE ), share(), startWith( window.matchMedia(BREAK_POINT_DYNAMIC).matches ? LARGE_DESKTOP : window.matchMedia(BREAK_POINT_3).matches ? DESKTOP : MOBILE ) ); // An observable keeping track of the drawer width. const drawerWidth$ = size$.pipe( map(size => (size >= LARGE_DESKTOP ? calcDrawerWidthDynamic() : calcDrawerWidth())) ); // An observable keeping track of the distance between // the middle point of the screen and the middle point of the drawer. const dist$ = drawerWidth$.pipe( withLatestFrom(size$), map( ([drawerWidth, s]) => s >= DESKTOP ? document.body.clientWidth / 2 - drawerWidth / 2 : document.body.clientWidth / 2 ) ); // An observable that keeps track of the range from where the drawer can be drawn. // Should be between 0 and the drawer's width on desktop; `getRange` on mobile. const range$ = drawerWidth$.pipe( withLatestFrom(size$), map(([drawerWidth, size]) => (size >= DESKTOP ? [0, drawerWidth] : getRange())) ); // Sliding the drawer's content between the middle point of the screen, // and the middle point of the drawer when closed. Observable.create(observer => (drawerEl.moveCallback = x => observer.next(x))) .pipe(withLatestFrom(dist$, size$)) .subscribe(([{ opacity }, dist, size]) => updateSidebar(size >= DESKTOP, dist, opacity)); // Setting `will-change` at the beginning of an interaction, and remove at the end. drawerEl.addEventListener("hy-drawer-prepare", () => { if (hasCSSOM) { sidebar.attributeStyleMap.set("will-change", "transform"); content.attributeStyleMap.set("will-change", "opacity"); } else { sidebar.style.willChange = "transform"; content.style.willChange = "opacity"; } }); drawerEl.addEventListener("hy-drawer-transitioned", () => { if (hasCSSOM) { sidebar.attributeStyleMap.delete("will-change"); content.attributeStyleMap.delete("will-change"); } else { sidebar.style.willChange = ""; content.style.willChange = ""; } }); // Adding the click callback to the menu button. // Calling `preventDefault` in iOS Safari, because otherwise it's causing the navbar to appear, // which ruins the animation. menuEl.addEventListener("click", e => { if (isMobileSafari) e.preventDefault(); window._drawer.toggle(); }); // Keeping track of the opened state. const opened$ = fromEvent(drawerEl, "hy-drawer-transitioned").pipe( map(e => e.detail), distinctUntilChanged(), tap(opened => !opened && removeIcon()) ); // Close the drawer on popstate, i.e. the back button. fromEvent(window, "popstate", { passive: true }) .pipe(subscribeWhen(opened$)) .subscribe(() => window._drawer.close()); // Save scroll position before the drawer gets initialized. const scrollTop = window.pageYOffset || document.body.scrollTop; // Start the drawer in `opened` state when the cover class is present, // and the user hasn't started scrolling already. const opened = drawerEl.classList.contains("cover") && scrollTop <= 0; // HACK: uuuugly drawerEl._peek$ = size$.pipe( map(size => { switch (size) { case LARGE_DESKTOP: return calcDrawerWidthDynamic(); case DESKTOP: return calcDrawerWidth(); case MOBILE: return 0.5 * rem(); } }) ); // We need the height of the darwer in case we need to reset the scroll position const drawerHeight = opened ? null : drawerEl.getBoundingClientRect().height; drawerEl.addEventListener( "hy-drawer-init", () => { // Show the icon indicating that the drawer can be drawn using touch gestures. setupIcon(); // Add a class to incidate that the drawer has been initialized. drawerEl.classList.add("loaded"); // Compensating for the change in layout after the drawer gets initialized. if (drawerHeight && scrollTop >= drawerHeight) { window.scrollTo(0, scrollTop - drawerHeight); } }, { once: true } ); dist$ .pipe( withLatestFrom(size$), skip(1) ) .subscribe(([dist, size]) => updateSidebar( size >= DESKTOP, dist, typeof drawerEl.opacity !== "undefined" ? drawerEl.opacity : opened ? 1 : 0 // HACK ) ); // Now we create the component. window._drawer = defineWebComponent(drawerEl, opened); // Keeping the drawer updated. range$.subscribe(range => (drawerEl.range = range)); }
return Rx.Observable.create(async observer => { const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chromium-')); const chromiumArgs = args({ userDataDir, viewport, verboseLogging: this.logger.isVerbose, disableSandbox: this.browserConfig.disableSandbox, proxyConfig: this.browserConfig.proxy, }); let browser; let page; try { browser = await puppeteer.launch({ userDataDir, executablePath: this.binaryPath, ignoreHTTPSErrors: true, args: chromiumArgs, env: { TZ: browserTimezone, }, }); page = await browser.newPage(); // All navigation/waitFor methods default to 30 seconds, // which can cause the job to fail even if we bump timeouts in // the config. Help alleviate errors like // "TimeoutError: waiting for selector ".application" failed: timeout 30000ms exceeded" page.setDefaultTimeout(this.queueTimeout); } catch (err) { observer.error(new Error(`Error spawning Chromium browser: [${err}]`)); throw err; } safeChildProcess( { async kill() { await browser.close(); }, }, observer ); // Register with a few useful puppeteer event handlers: // https://pptr.dev/#?product=Puppeteer&version=v1.10.0&show=api-event-error // https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page const stderr$ = Rx.fromEvent(page, 'console').pipe( filter(line => line._type === 'error'), map(line => line._text), share() ); const [consoleMessage$, message$] = stderr$.pipe( partition(msg => msg.match(/\[\d+\/\d+.\d+:\w+:CONSOLE\(\d+\)\]/)) ); const driver$ = Rx.of( new HeadlessChromiumDriver(page, { maxScreenshotDimension: this.browserConfig.maxScreenshotDimension, logger: this.logger, inspect: this.browserConfig.inspect, }) ); const processError$ = Rx.fromEvent(page, 'error').pipe( mergeMap(err => Rx.throwError(new Error(`Unable to spawn Chromium: [${err}]`))) ); const processPageError$ = Rx.fromEvent(page, 'pageerror').pipe( mergeMap(err => Rx.throwError(new Error(`Uncaught exception within the page: [${err}]`))) ); const processRequestFailed$ = Rx.fromEvent(page, 'requestfailed').pipe( mergeMap(req => { const failure = req.failure && req.failure(); if (failure) { return Rx.throwError( new Error(`Request to [${req.url()}] failed! [${failure.errorText}]`) ); } return Rx.throwError(new Error(`Unknown failure! [${JSON.stringify(req)}]`)); }) ); const processExit$ = Rx.fromEvent(browser, 'disconnected').pipe( mergeMap(code => Rx.throwError(new Error(`Chromium exited with: [${JSON.stringify({ code })}]`)) ) ); const nssError$ = message$.pipe( filter(line => line.includes('error while loading shared libraries: libnss3.so')), mergeMap(() => Rx.throwError(new Error(`You must install nss for Reporting to work`))) ); const fontError$ = message$.pipe( filter(line => line.includes('Check failed: InitDefaultFont(). Could not find the default font') ), mergeMap(() => Rx.throwError(new Error('You must install freetype and ttf-font for Reporting to work')) ) ); const noUsableSandbox$ = message$.pipe( filter(line => line.includes('No usable sandbox! Update your kernel')), mergeMap(() => Rx.throwError( new Error( compactWhitespace(` Unable to use Chromium sandbox. This can be disabled at your own risk with 'xpack.reporting.capture.browser.chromium.disableSandbox' `) ) ) ) ); const exit$ = Rx.merge( processError$, processPageError$, processRequestFailed$, processExit$, nssError$, fontError$, noUsableSandbox$ ); observer.next({ driver$, consoleMessage$, message$, exit$, }); // unsubscribe logic makes a best-effort attempt to delete the user data directory used by chromium return () => { this.logger.debug(`deleting chromium user data directory at [${userDataDir}]`); // the unsubscribe function isn't `async` so we're going to make our best effort at // deleting the userDataDir and if it fails log an error. rimraf(userDataDir, err => { if (err) { return this.logger.error( `error deleting user data directory at [${userDataDir}]: [${err}]` ); } }); }; });
return Rx.Observable.create(async observer => { const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chromium-')); const chromiumArgs = args({ userDataDir, viewport, verboseLogging: this.logger.isVerbose, disableSandbox: this.browserConfig.disableSandbox, proxyConfig: this.browserConfig.proxy, }); let chromium; let page; try { chromium = await puppeteer.launch({ userDataDir, executablePath: this.binaryPath, ignoreHTTPSErrors: true, args: chromiumArgs, }); page = await chromium.newPage(); } catch (err) { observer.error(new Error(`Caught error spawning Chromium`)); return; } safeChildProcess({ async kill() { await chromium.close(); } }, observer); const stderr$ = Rx.fromEvent(page, 'console').pipe( filter(line => line._type === 'error'), map(line => line._text), share() ); const [consoleMessage$, message$] = stderr$.pipe( partition(msg => msg.match(/\[\d+\/\d+.\d+:\w+:CONSOLE\(\d+\)\]/)) ); const driver$ = Rx.of(new HeadlessChromiumDriver(page, { maxScreenshotDimension: this.browserConfig.maxScreenshotDimension, logger: this.logger })); const processError$ = Rx.fromEvent(page, 'error').pipe( map((err) => this.logger.error(err)), mergeMap(() => Rx.throwError(new Error(`Unable to spawn Chromium`))), ); const processExit$ = Rx.fromEvent(chromium, 'disconnected').pipe( mergeMap((err) => Rx.throwError(new Error(`Chromium exited with code: ${err}. ${JSON.stringify(err)}`))) ); const nssError$ = message$.pipe( filter(line => line.includes('error while loading shared libraries: libnss3.so')), mergeMap(() => Rx.throwError(new Error(`You must install nss for Reporting to work`))) ); const fontError$ = message$.pipe( filter(line => line.includes('Check failed: InitDefaultFont(). Could not find the default font')), mergeMap(() => Rx.throwError(new Error('You must install freetype and ttf-font for Reporting to work'))) ); const noUsableSandbox$ = message$.pipe( filter(line => line.includes('No usable sandbox! Update your kernel')), mergeMap(() => Rx.throwError(new Error(compactWhitespace(` Unable to use Chromium sandbox. This can be disabled at your own risk with 'xpack.reporting.capture.browser.chromium.disableSandbox' `)))) ); const exit$ = Rx.merge(processError$, processExit$, nssError$, fontError$, noUsableSandbox$); observer.next({ driver$, consoleMessage$, message$, exit$ }); // unsubscribe logic makes a best-effort attempt to delete the user data directory used by chromium return () => { this.logger.debug(`deleting chromium user data directory at ${userDataDir}`); // the unsubscribe function isn't `async` so we're going to make our best effort at // deleting the userDataDir and if it fails log an error. rimraf(userDataDir, (err) => { if (err) { return this.logger.error(`error deleting user data directory at ${userDataDir}: ${err}`); } }); }; });
const newHandler = (argument, context) => { const argumentJson = stableStringify(argument); const maybeJob = runs.get(argumentJson); if (maybeJob) { return maybeJob; } const run = handler(argument, context).pipe(replayMessages ? operators_1.shareReplay() : operators_1.share()); runs.set(argumentJson, run); return run; };
function setupDrawer() { const resize$ = fromEvent(window, "resize", { passive: true }).pipe(share()); // An observable keeping track of whether the window size is greater than `BREAK_POINT_3`. const isDesktop$ = resize$.pipe( map(() => window.matchMedia(BREAK_POINT_3).matches), distinctUntilChanged(), share(), startWith(window.matchMedia(BREAK_POINT_3).matches) ); // An observable keeping track of the drawer width. const drawerWidth$ = resize$.pipe( startWith({}), map( () => window.matchMedia(BREAK_POINT_DYNAMIC).matches ? calcDrawerWidthDynamic() : calcDrawerWidth() ) ); // An observable keeping track of the distance between // the middle point of the screen and the middle point of the drawer. const dist$ = drawerWidth$.pipe( map( drawerWidth => window.matchMedia(BREAK_POINT_3).matches ? document.body.clientWidth / 2 - drawerWidth / 2 : document.body.clientWidth / 2 ) ); // An observable that keeps track of the range from where the drawer can be drawn. // Should be between 0 and the drawer's width on desktop; `getRange` on mobile. const range$ = drawerWidth$.pipe( withLatestFrom(isDesktop$), map(([drawerWidth, isDesktop]) => (isDesktop ? [0, drawerWidth] : getRange())) ); // Sliding the drawer's content between the middle point of the screen, // and the middle point of the drawer when closed. Observable.create(observer => (drawerEl.moveCallback = observer.next.bind(observer))) .pipe(withLatestFrom(dist$, isDesktop$)) .subscribe(([{ opacity }, dist, isDesktop]) => updateSidebar(dist, opacity, isDesktop)); // Setting `will-change` at the beginning of an interaction, and remove at the end. drawerEl.addEventListener("hy-drawer-prepare", () => { if (hasCSSOM) { sidebar.attributeStyleMap.set("will-change", "transform"); sticky.attributeStyleMap.set("will-change", "opacity"); } else { sidebar.style.willChange = "transform"; sticky.style.willChange = "opacity"; } }); drawerEl.addEventListener("hy-drawer-transitioned", () => { if (hasCSSOM) { sidebar.attributeStyleMap.delete("will-change"); sticky.attributeStyleMap.delete("will-change"); } else { sidebar.style.willChange = ""; sticky.style.willChange = ""; } }); // Adding the click callback to the menu button. // Calling `preventDefault` in iOS Safari, because otherwise it's causing the navbar to appear, // which ruins the animation. menuEl.addEventListener("click", e => { if (isMobileSafari) e.preventDefault(); window._drawer.toggle(); }); // Keeping track of the opened state. const opened$ = fromEvent(drawerEl, "hy-drawer-transitioned").pipe( map(e => e.detail), distinctUntilChanged(), tap(opened => !opened && removeIcon()) ); // Close the drawer on popstate, i.e. the back button. fromEvent(window, "popstate", { passive: true }) .pipe(subscribeWhen(opened$)) .subscribe(() => window._drawer.close()); // Save scroll position before the drawer gets initialized. const scrollTop = window.pageYOffset || document.body.scrollTop; // Start the drawer in `opened` state when the cover class is present, // and the user hasn't started scrolling already. const opened = drawerEl.classList.contains("cover") && scrollTop <= 0; // We need the height of the darwer in case we need to reset the scroll position let drawerHeight; if (!opened) { drawerHeight = drawerEl.getBoundingClientRect().height; } drawerEl.addEventListener( "hy-drawer-init", () => { // Keeping the drawer updated. range$.subscribe(range => (drawerEl.range = range)); // Show the icon indicating that the drawer can be drawn using touch gestures. setupIcon(); // Add a class to incidate that the drawer has been initialized. drawerEl.classList.add("loaded"); // The drawer height is `100vh` before the drawer is initialized and is now set to 0. // We remove `innerHeight` from the old scroll position to prevent the content form "jumping". if (!opened && scrollTop >= drawerHeight) { window.scrollTo(0, scrollTop - drawerHeight); } }, { once: true } ); // HACK let firstRun = true; dist$.pipe(withLatestFrom(isDesktop$)).subscribe(([dist, isDesktop]) => { if (firstRun) { firstRun = false; updateSidebar(dist, opened ? 1 : 0, isDesktop); } else { updateSidebar(dist, drawerEl.opacity, isDesktop); } }); // Now we create the component. window._drawer = defineWebComponent(drawerEl, opened); }
async function open(deviceOrId: Device | string, needsReconnect: boolean) { let device; if (typeof deviceOrId === "string") { if (transportsCache[deviceOrId]) { logSubject.next({ type: "verbose", message: "Transport in cache, using that." }); return transportsCache[deviceOrId]; } logSubject.next({ type: "verbose", message: `open(${deviceOrId})` }); await awaitsBleOn(bleManager); if (!device) { // works for iOS but not Android const devices = await bleManager.devices([deviceOrId]); logSubject.next({ type: "verbose", message: `found ${devices.length} devices` }); [device] = devices; } if (!device) { const connectedDevices = await bleManager.connectedDevices( getBluetoothServiceUuids() ); const connectedDevicesFiltered = connectedDevices.filter( d => d.id === deviceOrId ); logSubject.next({ type: "verbose", message: `found ${connectedDevicesFiltered.length} connected devices` }); [device] = connectedDevicesFiltered; } if (!device) { logSubject.next({ type: "verbose", message: `connectToDevice(${deviceOrId})` }); try { device = await bleManager.connectToDevice(deviceOrId, connectOptions); } catch (e) { if (e.errorCode === BleErrorCode.DeviceMTUChangeFailed) { connectOptions = {}; device = await bleManager.connectToDevice(deviceOrId); } else { throw e; } } } if (!device) { throw new CantOpenDevice(); } } else { device = deviceOrId; } if (!(await device.isConnected())) { logSubject.next({ type: "verbose", message: "not connected. connecting..." }); try { await device.connect(connectOptions); } catch (e) { if (e.errorCode === BleErrorCode.DeviceMTUChangeFailed) { connectOptions = {}; await device.connect(); } else { throw e; } } } await device.discoverAllServicesAndCharacteristics(); let res = retrieveInfos(device); let characteristics; if (!res) { for (const uuid of getBluetoothServiceUuids()) { try { characteristics = await device.characteristicsForService(uuid); res = getInfosForServiceUuid(uuid); break; } catch (e) { // we attempt to connect to service } } } if (!res) { throw new TransportError("service not found", "BLEServiceNotFound"); } const { deviceModel, serviceUuid, writeUuid, notifyUuid } = res; if (!characteristics) { characteristics = await device.characteristicsForService(serviceUuid); } if (!characteristics) { throw new TransportError("service not found", "BLEServiceNotFound"); } let writeC; let notifyC; for (const c of characteristics) { if (c.uuid === writeUuid) { writeC = c; } else if (c.uuid === notifyUuid) { notifyC = c; } } if (!writeC) { throw new TransportError( "write characteristic not found", "BLEChracteristicNotFound" ); } if (!notifyC) { throw new TransportError( "notify characteristic not found", "BLEChracteristicNotFound" ); } if (!writeC.isWritableWithResponse) { throw new TransportError( "write characteristic not writableWithResponse", "BLEChracteristicInvalid" ); } if (!notifyC.isNotifiable) { throw new TransportError( "notify characteristic not notifiable", "BLEChracteristicInvalid" ); } logSubject.next({ type: "verbose", message: `device.mtu=${device.mtu}` }); const notifyObservable = monitorCharacteristic(notifyC).pipe( tap(value => { logSubject.next({ type: "ble-frame-read", message: value.toString("hex") }); }), share() ); const notif = notifyObservable.subscribe(); const transport = new BluetoothTransport( device, writeC, notifyObservable, deviceModel ); const onDisconnect = e => { transport.notYetDisconnected = false; notif.unsubscribe(); disconnectedSub.remove(); delete transportsCache[transport.id]; logSubject.next({ type: "verbose", message: `BleTransport(${transport.id}) disconnected` }); transport.emit("disconnect", e); }; transportsCache[transport.id] = transport; const disconnectedSub = device.onDisconnected(e => { if (!transport.notYetDisconnected) return; onDisconnect(e); }); let beforeMTUTime = Date.now(); try { await transport.inferMTU(); } finally { let afterMTUTime = Date.now(); if (reconnectionConfig) { // workaround for #279: we need to open() again if we come the first time here, // to make sure we do a disconnect() after the first pairing time // because of a firmware bug if (afterMTUTime - beforeMTUTime < reconnectionConfig.pairingThreshold) { needsReconnect = false; // (optim) there is likely no new pairing done because mtu answer was fast. } if (needsReconnect) { // necessary time for the bonding workaround await BluetoothTransport.disconnect(transport.id).catch(() => {}); await delay(reconnectionConfig.delayAfterFirstPairing); } } else { needsReconnect = false; } } if (needsReconnect) { return open(device, false); } return transport; }
export function fromRef(ref) { return _fromRef(ref).pipe(share()); }
bindMessageHandlers(client, handlers, transform) { const close$ = rxjs_1.fromEvent(client, 'close').pipe(operators_1.share(), operators_1.first()); const source$ = rxjs_1.fromEvent(client, 'message').pipe(operators_1.mergeMap(data => this.bindMessageHandler(data, handlers, transform).pipe(operators_1.filter(result => result))), operators_1.takeUntil(close$)); source$.subscribe(response => client.send(JSON.stringify(response))); }
/** * Returns an Observable which produces the string contents of the given URL. Results may be * cached, so future calls with the same URL may not cause another HTTP request. * @param {?} safeUrl * @return {?} */ _fetchUrl(safeUrl) { if (!this._httpClient) { throw getMatIconNoHttpProviderError(); } if (safeUrl == null) { throw Error(`Cannot fetch icon from URL "${safeUrl}".`); } const /** @type {?} */ url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, safeUrl); if (!url) { throw getMatIconFailedToSanitizeUrlError(safeUrl); } // Store in-progress fetches to avoid sending a duplicate request for a URL when there is // already a request in progress for that URL. It's necessary to call share() on the // Observable returned by http.get() so that multiple subscribers don't cause multiple XHRs. const /** @type {?} */ inProgressFetch = this._inProgressUrlFetches.get(url); if (inProgressFetch) { return inProgressFetch; } // TODO(jelbourn): for some reason, the `finalize` operator "loses" the generic type on the // Observable. Figure out why and fix it. const /** @type {?} */ req = this._httpClient.get(url, { responseType: 'text' }).pipe(finalize(() => this._inProgressUrlFetches.delete(url)), share()); this._inProgressUrlFetches.set(url, req); return req; }